Background
PR #211 attempted to add biome linting/formatting. The formatting and config were fine, but bunx biome lint --write --unsafe modified 59 files and introduced silent regressions in React hook dependency arrays that multiple review passes failed to fully catch. PR was closed.
What went wrong
--unsafe modified dep arrays — biome's useExhaustiveDependencies rule added/removed deps from useEffect/useMemo/useCallback arrays across the entire codebase
- TDZ crashes — Some added deps referenced
const variables defined later in the component. React evaluates dep arrays when the hook call executes, so forward references to const hit the Temporal Dead Zone and crash: "Cannot access 'X' before initialization"
- Functional regressions — Some added deps were plain functions (not
useCallback), which get recreated every render. Adding them as deps caused effects to re-run every render, destroying stateful objects like the web-highlighter instance (broke text selection entirely)
- Suppression masking — We added
biome-ignore comments with noInvalidUseBeforeDeclaration to silence the TDZ warnings, not realizing the suppression hides a runtime crash, not just a lint pedantry
Recommended approach for next attempt
Do
- Format first, lint second —
biome format --write . in one commit, then assess lint findings separately
- Never use
--write --unsafe for lint — review every lint suggestion manually, especially useExhaustiveDependencies
- Disable
useExhaustiveDependencies entirely — this rule is the source of all the regressions. The codebase has intentional dep array choices (extra deps as triggers, omitted deps to prevent loops). Biome's rule doesn't understand these patterns. If we want exhaustive deps checking, use React's own eslint plugin which handles these edge cases better
- Audit dep arrays with a script — after any automated changes, diff every
}, [ line against main and verify each change
- Test the production build — TDZ errors only manifest in bundled builds (or on first render in dev), not in lint passes
Don't
- Don't run
--unsafe on the full codebase
- Don't suppress
noInvalidUseBeforeDeclaration — if biome flags it, the dep needs to be removed, not suppressed
- Don't add plain functions to dep arrays without wrapping them in
useCallback first
Config that worked
CI workflow that worked
name: CI
on:
pull_request:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: biomejs/setup-biome@v2
- run: biome ci .
Salvageable work from PR #211
These commits from the closed PR were clean and should be cherry-picked into separate PRs:
Background
PR #211 attempted to add biome linting/formatting. The formatting and config were fine, but
bunx biome lint --write --unsafemodified 59 files and introduced silent regressions in React hook dependency arrays that multiple review passes failed to fully catch. PR was closed.What went wrong
--unsafemodified dep arrays — biome'suseExhaustiveDependenciesrule added/removed deps fromuseEffect/useMemo/useCallbackarrays across the entire codebaseconstvariables defined later in the component. React evaluates dep arrays when the hook call executes, so forward references toconsthit the Temporal Dead Zone and crash: "Cannot access 'X' before initialization"useCallback), which get recreated every render. Adding them as deps caused effects to re-run every render, destroying stateful objects like theweb-highlighterinstance (broke text selection entirely)biome-ignorecomments withnoInvalidUseBeforeDeclarationto silence the TDZ warnings, not realizing the suppression hides a runtime crash, not just a lint pedantryRecommended approach for next attempt
Do
biome format --write .in one commit, then assess lint findings separately--write --unsafefor lint — review every lint suggestion manually, especiallyuseExhaustiveDependenciesuseExhaustiveDependenciesentirely — this rule is the source of all the regressions. The codebase has intentional dep array choices (extra deps as triggers, omitted deps to prevent loops). Biome's rule doesn't understand these patterns. If we want exhaustive deps checking, use React's own eslint plugin which handles these edge cases better}, [line againstmainand verify each changeDon't
--unsafeon the full codebasenoInvalidUseBeforeDeclaration— if biome flags it, the dep needs to be removed, not suppresseduseCallbackfirstConfig that worked
CI workflow that worked
Salvageable work from PR #211
These commits from the closed PR were clean and should be cherry-picked into separate PRs:
0916809—refactor: extract shared server handlers(closes Refactor: Extract shared route handlers and modularize plan server #210)9704073—chore: remove dead code (knip findings)7a8d9df—refactor: extract shared CSS theme and formatTimestamp utility