chore(lint): adopt Biome 2.5.5 / ultracite 7.9.4 and set policy for its new rules - #142
Merged
Merged
Conversation
…ts new rules Splits the lint-policy half out of the pending non-major dependency batch. That bump quietly enables 15 new rules, which turn `pnpm lint` into 5,532 diagnostics across 609 files — enough to block the whole update PR on something unrelated to the packages actually being updated. Adopted (real defects, fixed here): - correctness/noUnsafeOptionalChaining (2): two fetch stubs did `(init?.headers as Record<string,string>).Authorization`, which throws a TypeError instead of failing the assertion when a call passes no init. Now optional all the way through. - complexity/noUselessReturn (12): trailing `return;` removed. Each one checked for loop context first — a `return` inside a loop is NOT a no-op, and one hit in notification-prefs sat right above a `for`, though it turned out to be the tail of a nested function. Declined, each with rationale in biome.jsonc. The two that matter: - assist/source/useSortedKeys (3,556): object-literal key order is OBSERVABLE in our data. Snapshots are stored as JSON.stringify'd blobs and getChangelog compares wayback vs live rows for BYTE-IDENTICAL equality to set `matches_live_sync`. Alphabetising keys would silently break that against every row already in users' databases. - performance/noAwaitInLoops (62): sequential awaits here are deliberate — the scrapers and bulk runners walk apps one at a time to stay under Apple's rate limits. Promise.all() would be an outage. The rest (noJsxPropsBind, noLeakedRender, noUnnecessaryConditions, noEqualsToNull, useArraySortCompare, noNestedPromises, noShadow, three stylistic rules) are idiomatic-here or pure churn; useErrorCause is flagged as worth adopting deliberately in its own PR. Verified: lint exits 0, typecheck, 441 unit tests, i18n parity, audit clean, production build, full Playwright suite 41/41, and build-storybook (the Babel compat canary). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why this is its own PR
The pending non-major dependency batch (#125) bumps
ultracite7.8.4 → 7.9.4 and@biomejs/biome2.5.2 → 2.5.5. That quietly enables 15 new rules, takingpnpm lintfrom clean to 5,532 diagnostics across 609 files — which would block a routine dependency update on a lint-policy argument.So: policy here, packages there. Once this merges, #125 rebases and its Biome/ultracite entries become a no-op.
Adopted — real defects, fixed here
correctness/noUnsafeOptionalChaining(2) — genuine bug in two fetch stubs:If a call passes no
init, the?.short-circuits toundefinedand the property access throws aTypeErrorfrom inside the stub — so a wrong-auth-header regression would surface as a confusing crash instead of a clean assertion failure. Now optional the whole way through.complexity/noUselessReturn(12) — trailingreturn;removed.I checked every one for loop context before accepting the autofix:
returninside a loop is not a no-op, and dropping it would convert "exit the function" into "continue iterating". One hit innotification-prefs/route.tssat directly above aforloop — it turned out to be the tail of a nestedreadBool()function, so it was safe, but it's exactly the case that would have been a silent behaviour change.Declined — with rationale in
biome.jsoncTwo of these are substantive:
assist/source/useSortedKeys(3,556) — alphabetises the keys of every object literal. Beyond buryinggit blameacross ~600 files, object-literal order is observable in our data: snapshots are persisted asJSON.stringify'd blobs inprivacy_snapshots.snapshot_json, andgetChangelogcompares a wayback row against an adjacent live row for byte-identical equality to setmatches_live_sync. Reordering keys in the objects that feed a snapshot changes those bytes and silently breaks that comparison against every row already sitting in users' databases.performance/noAwaitInLoops(62) — sequential awaits here are deliberate. The scrapers, bulk sync, wayback import and policy runners walk apps one at a time specifically to stay under Apple's rate limits (see the 429 handling inlib/sync-bulk-runner.ts).Promise.all()would be an outage, not an optimisation.The rest are idiomatic-in-this-codebase or pure churn, each documented inline:
noJsxPropsBinduseCallbackwrappers for an unmeasured costnoLeakedRender{cond && …}guards are booleans /.length > 0, so the0-render hazard doesn't applynoUnnecessaryConditionsnoIncrementDecrement/useDestructuring/useConsistentMethodSignaturesnoShadowapp,row,err) reusing an outer name in a tighter scopeuseErrorCausenoEqualsToNullx == nullis the intended "null or undefined" idiomuseArraySortComparenoNestedPromisesawait res.json().catch(() => null), the standard safe-parseVerification
biome checkexits 0 across all 609 filespnpm typecheckclean · 441 unit tests pass · i18n parity (4,763 keys) ·pnpm audit --prodcleanpnpm build-storybooksucceeds — the Babel compat canary called out inpnpm-workspace.yaml🤖 Generated with Claude Code