Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 16 additions & 28 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,37 +48,25 @@ repos:
types_or: [ts, svelte]
pass_filenames: false

- id: check-biome-pins
name: check biome version pins agree
entry: python3 scripts/check_biome_pins.py
# Runs the Biome that npm installed, via the same `npm run lint` a
# developer runs by hand, so there is exactly one Biome version in the
# repo: frontend/package.json. It used to be declared here as well (a
# `rev` plus an `additional_dependencies` pin) because pre-commit
# fetches its own hermetic copy and never looks at frontend/
# node_modules -- which is how this repo once enforced 2.2.4 against a
# declared 2.5.11, nine releases apart and silently (issue #81).
#
# What Biome looks at is now biome.json's files.includes, not a regex
# here. Keeping that scoping in this file meant `npm run lint` had no
# scoping at all: it swept frontend/.svelte-kit/ and reported ~5600
# diagnostics, so nobody could use it.
- id: biome-check
name: biome check
entry: bash -c "cd frontend && npm run lint"
language: system
files: '^(frontend/package(-lock)?\.json|\.pre-commit-config\.yaml|biome\.json)$'
types_or: [ts, tsx, javascript, jsx, json, svelte]
pass_filenames: false

# Both pins below must match "@biomejs/biome" in frontend/package.json.
# The rev is what actually gates CI; the npm pin only governs `npm run
# lint`, which CI never runs. When they drift, the gate silently enforces
# the older ruleset. The check-biome-pins hook above fails the run if so.
- repo: https://github.com/biomejs/pre-commit
rev: 'v2.5.13'
hooks:
- id: biome-check
# Scoped to source files rather than a bare ^frontend/. Biome gained
# HTML/SVG support after 2.2.4, so a bare prefix now sweeps in files
# the old pin silently skipped:
# - app.html / favicon.svg: attributes get reordered, a trailing
# space is emitted before '>', the pre-paint theme script's
# function expression is rewritten, and noSvgWithoutTitle fails
# on the favicon. Linting markup is a separate decision from
# this version bump.
# - app.css: the only CSS file, and biome.json already excludes it
# twice. Passing it explicitly bypasses experimentalScannerIgnores
# and the parser rejects Tailwind's @plugin before the override's
# disabled linter/formatter applies.
files: '^frontend/.*\.(jsx?|tsx?|c(js|ts)|m(js|ts)|jsonc?|svelte)$'
additional_dependencies:
- '@biomejs/biome@2.5.13'

ci:
autofix_commit_msg: |
[pre-commit.ci] auto fixes from pre-commit hooks
Expand Down
65 changes: 34 additions & 31 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,33 +37,34 @@ pre-commit run --all-files
argument, not a pytest suite — it errors under plain collection. Run
`pytest tests/ --ignore=tests/test_workers.py`.

### Biome's version is pinned in five places

They must all agree, and `scripts/check_biome_pins.py` (wired into pre-commit)
fails the run when they don't:

| Location |
| --- |
| `frontend/package.json` → `@biomejs/biome` |
| `frontend/package-lock.json` → resolved entry |
| `.pre-commit-config.yaml` → `rev` |
| `.pre-commit-config.yaml` → `additional_dependencies` |
| `biome.json` → `$schema` URL |

The pre-commit `rev`/`additional_dependencies` pair is what actually gates CI:
the hook runs its own pinned copy of Biome in an isolated environment and never
touches `frontend/node_modules`. `npm run lint` uses the npm pin but CI never
invokes it. When these drift, **nothing fails** — the gate just silently
enforces an older ruleset. That is how the repo once ended up enforcing 2.2.4
against a declared 2.5.11, nine minor releases apart.

Dependabot bumps the first two and **cannot** touch the other three, so expect
`check-biome-pins` to fail on the first run after a Biome bump. Update all five
together.

`frontend/node_modules` can also be stale relative to the lockfile — run
`npm ci` before trusting any local lint result. A stale tree is what masked the
nine-version discrepancy above.
### Biome's version lives in exactly one place

`frontend/package.json` → `@biomejs/biome` (with the lockfile npm maintains
alongside it). Nothing else declares a Biome version, so Dependabot can bump it
unaided and there is nothing to keep in sync.

It was not always so. Biome used to run through the remote
`biomejs/pre-commit` hook, which fetches its own hermetic copy and never looks
at `frontend/node_modules`. That copy needed its own version — a `rev` plus an
`additional_dependencies` pin — which, with the npm pin, the lockfile and
`biome.json`'s `$schema` URL, made five declarations of one fact. When they
drifted **nothing failed**: the gate just quietly enforced the older ruleset.
That is how this repo once enforced 2.2.4 against a declared 2.5.11, nine
releases apart (issue #81). A guard script was added to catch the drift, then
removed along with the drift itself.

Biome now runs as a `repo: local` hook invoking `npm run lint`, the same
command a developer runs by hand — the pattern `svelte-check` already used.
Two consequences worth knowing:

- **The hook needs `frontend/node_modules` to exist**, so run `npm ci` before
trusting any local lint result. A stale tree is what masked the nine-version
discrepancy above. CI installs it before running pre-commit.
- **What Biome looks at is `biome.json`'s `files.includes`**, not a regex in
`.pre-commit-config.yaml`. That scoping used to live in the hook, which meant
`npm run lint` had none: it swept `frontend/.svelte-kit/` and reported ~5600
diagnostics, so the command was effectively unusable. Change the scope in
`biome.json` and both the hook and the npm script follow.

---

Expand Down Expand Up @@ -155,9 +156,12 @@ for a webhook to come back around.
several releases, review the diagnostics — a bump once began reformatting
`app.html`/`favicon.svg` (reordered attributes, a trailing space before `>`,
a rewritten pre-paint script) and erroring on the favicon, none of it wanted.
- **Prefer scoped lint globs over broad prefixes.** The Biome hook is scoped to
source extensions rather than a bare `^frontend/`, because tools grow support
for new file types and a broad prefix silently widens what they touch.
- **Keep lint scoping in the tool's own config, not the hook's.** `biome.json`
names the source extensions it covers rather than a bare `frontend/`, because
tools grow support for new file types and a broad prefix silently widens what
they touch — Biome gained HTML/SVG support after 2.2.4, which would sweep in
`app.html` and `favicon.svg`. Scoping there rather than in the hook also
keeps `npm run lint` and CI enforcing the same set.
- **Ship user-visible changes with their docs.** See below — a change a user can
see is not finished until the page describing it says so.

Expand Down Expand Up @@ -222,7 +226,6 @@ frontend/
src/routes/config/+page.svelte # Settings UI
src/lib/types.ts # Types matching backend API responses
scripts/
check_biome_pins.py # Lint-gate version-drift guard
fix_container_mismatches.py # One-off library sweep, dry-run by default
docs/ # User-facing reference
```
Expand Down
30 changes: 26 additions & 4 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
{
"$schema": "https://biomejs.dev/schemas/2.5.13/schema.json",
"$schema": "./frontend/node_modules/@biomejs/biome/configuration_schema.json",
"files": {
"ignoreUnknown": true,
"maxSize": 4194304,
"experimentalScannerIgnores": ["frontend/src/app.css"]
"includes": [
"frontend/**/*.js",
"frontend/**/*.jsx",
"frontend/**/*.cjs",
"frontend/**/*.mjs",
"frontend/**/*.ts",
"frontend/**/*.tsx",
"frontend/**/*.cts",
"frontend/**/*.mts",
"frontend/**/*.json",
"frontend/**/*.jsonc",
"frontend/**/*.svelte",
"!frontend/node_modules/**",
"!frontend/.svelte-kit/**",
"!frontend/build/**"
],
"experimentalScannerIgnores": [
"frontend/src/app.css"
]
},
"formatter": {
"enabled": true,
Expand Down Expand Up @@ -32,7 +50,9 @@
},
"overrides": [
{
"includes": ["frontend/src/app.css"],
"includes": [
"frontend/src/app.css"
],
"linter": {
"enabled": false
},
Expand All @@ -41,7 +61,9 @@
}
},
{
"includes": ["**/*.svelte"],
"includes": [
"**/*.svelte"
],
"linter": {
"rules": {
"correctness": {
Expand Down
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"lint": "biome check . --config-path=../biome.json",
"lint:fix": "biome check . --write --config-path=../biome.json"
"lint": "cd .. && biome check frontend",
"lint:fix": "cd .. && biome check frontend --write"
},
"dependencies": {
"daisyui": "^5.7.37"
Expand Down
126 changes: 0 additions & 126 deletions scripts/check_biome_pins.py

This file was deleted.

Loading