Code Quality
GospeLib enforces code quality through linters, formatters, and type checkers for each language. This guide covers how to run them and what standards are enforced.
Quick Reference
# Run all linters (TS/JS projects via Nx)
pnpm lint
# Format all files
pnpm format
# Type-check all TS projects
pnpm typecheck
TypeScript / JavaScript
ESLint
Flat config in eslint.config.mjs with typescript-eslint strict preset:
# Lint all TS/JS projects
pnpm lint
# Lint only affected projects
pnpm nx affected -t lint --parallel=4
# Auto-fix
pnpm nx run <project>:lint --fix
Key rules enforced:
| Rule | Level | Notes |
|---|---|---|
no-unused-vars | error | Ignores variables prefixed with _ |
no-explicit-any | warn | Prefer typed alternatives |
strict TypeScript | enabled | Via tsconfig.base.json |
Prettier
Configured in the root with:
- Print width: 100
- Indent: 2 spaces
- Single quotes
- Semicolons enabled
# Format all supported files
pnpm format
# Check formatting without writing
pnpm prettier --check .
TypeScript Strict Mode
The base tsconfig.base.json enables:
strict: truenoUnusedLocals: truenoUnusedParameters: truenoFallthroughCasesInSwitch: trueverbatimModuleSyntax: trueisolatedModules: true
# Run type checking across all projects
pnpm typecheck
Python
Ruff (Linting + Formatting)
Ruff handles both linting and formatting for all Python services. Configuration is per-service in pyproject.toml:
[tool.ruff]
line-length = 100
target-version = "py312"
# Lint
cd services/content && uv run ruff check .
# Lint with auto-fix
cd services/content && uv run ruff check . --fix
# Format
cd services/content && uv run ruff format .
mypy (Type Checking)
mypy runs in strict mode:
[tool.mypy]
strict = true
cd services/content && uv run mypy .
Combined lint check
# Lint + type-check a Python service
cd services/content && uv run ruff check . && uv run mypy .
Use the VS Code tasks for convenience:
- Lint: Python (Content) —
cd services/content && uv run ruff check . && uv run mypy . - Lint: Python (AI) —
cd services/ai && uv run ruff check . && uv run mypy . - Lint: Python (Ingest) —
cd services/ingest && uv run ruff check . && uv run mypy .
Go
go vet + golangci-lint
Each Go service has a .golangci.yml enabling these linters:
- govet, errcheck, staticcheck, unused, gosimple
- ineffassign, typecheck, gocritic
- Line length: 120 characters
# Vet + lint a Go service
cd services/gateway && go vet ./... && golangci-lint run
Formatting
Go uses gofmt (tabs, not spaces) — enforced by golangci-lint:
# Format Go code
gofmt -w services/gateway/
EditorConfig
The .editorconfig at the repo root normalizes editor settings:
| Scope | Indent | Size | EOL |
|---|---|---|---|
Default (*) | spaces | 2 | LF |
*.py | spaces | 4 | LF |
*.go | tabs | 4 | LF |
*.md | — | — | LF (no trailing whitespace trim) |
Pre-Commit Pipeline
Every commit runs through this pipeline automatically:
git commit
→ Husky pre-commit hook
→ lint-staged
→ *.{js,jsx,ts,tsx,mjs,cjs,mts,cts} → eslint --fix → prettier --write
→ *.{json,md,yml,yaml,css,scss} → prettier --write
→ Husky commit-msg hook
→ commitlint (Conventional Commits)
Commit message format
Commits must follow Conventional Commits enforced by commitlint:
feat(content): add witness endpoint
fix(gateway): handle missing JWT gracefully
docs(guides): add testing guide
Valid scopes: web, mobile, admin, gateway, content, auth, billing, ai, notifications, ingest, ui, sdk, types, config, infra, ci, deps
Use pnpm commit for an interactive commit prompt (Commitizen).
CI Integration
The CI pipeline runs lint checks on every PR:
# What CI runs
pnpm nx affected -t lint --parallel=4
pnpm nx affected -t typecheck --parallel=4
All lint and type-check failures block merge.
Troubleshooting
ESLint reports errors in files I didn't change
Run pnpm nx affected -t lint to lint only files affected by your branch. If the issue persists, the base branch may have introduced a new rule.
mypy reports errors in third-party packages
Add type stubs or ignore the specific import:
import some_untyped_lib # type: ignore[import-untyped]
golangci-lint is slow
Increase the timeout in .golangci.yml:
run:
timeout: 5m