Skip to content

chore(lint): adopt Biome 2.5.5 / ultracite 7.9.4 and set policy for its new rules - #142

Merged
AdamXweb merged 1 commit into
mainfrom
chore/biome-2.5.5-rule-policy
Jul 29, 2026
Merged

chore(lint): adopt Biome 2.5.5 / ultracite 7.9.4 and set policy for its new rules#142
AdamXweb merged 1 commit into
mainfrom
chore/biome-2.5.5-rule-policy

Conversation

@adamXbot

Copy link
Copy Markdown
Collaborator

Why this is its own PR

The pending non-major dependency batch (#125) bumps ultracite 7.8.4 → 7.9.4 and @biomejs/biome 2.5.2 → 2.5.5. That quietly enables 15 new rules, taking pnpm lint from 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.

Heads-up for anyone re-checking these numbers: Biome caps output at 20 diagnostics by default, so a plain biome check makes this look like a 20-problem chore. Pass --max-diagnostics=9000 to see the real scale.

Adopted — real defects, fixed here

correctness/noUnsafeOptionalChaining (2) — genuine bug in two fetch stubs:

(init?.headers as Record<string, string>).Authorization

If a call passes no init, the ?. short-circuits to undefined and the property access throws a TypeError from 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) — trailing return; removed.

I checked every one for loop context before accepting the autofix: return inside a loop is not a no-op, and dropping it would convert "exit the function" into "continue iterating". One hit in notification-prefs/route.ts sat directly above a for loop — it turned out to be the tail of a nested readBool() function, so it was safe, but it's exactly the case that would have been a silent behaviour change.

Declined — with rationale in biome.jsonc

Two of these are substantive:

assist/source/useSortedKeys (3,556) — alphabetises the keys of every object literal. Beyond burying git blame across ~600 files, object-literal order is observable in our data: snapshots are persisted as JSON.stringify'd blobs in privacy_snapshots.snapshot_json, and getChangelog compares a wayback row against an adjacent live row for byte-identical equality to set matches_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 in lib/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:

Rule Hits Why off
noJsxPropsBind 682 Inline arrow props are this UI's idiom; ~700 useCallback wrappers for an unmeasured cost
noLeakedRender 434 Our {cond && …} guards are booleans / .length > 0, so the 0-render hazard doesn't apply
noUnnecessaryConditions 396 Type-aware, and our types are optimistic by design — we parse App Store JSON, archive.org captures, AI responses and user CSVs. Deleting "redundant" guards is how a resilient parser starts crashing
noIncrementDecrement / useDestructuring / useConsistentMethodSignatures 290 Stylistic only, consistent with the convention-driven entries already in this file
noShadow 40 Small callback params (app, row, err) reusing an outer name in a tighter scope
useErrorCause 22 The one with real merit — flagged in-config as worth adopting deliberately in its own PR rather than as a side effect of a version bump
noEqualsToNull 21 x == null is the intended "null or undefined" idiom
useArraySortCompare 12 Checked individually — every one sorts strings, where the default lexicographic order is correct
noNestedPromises 3 All three are await res.json().catch(() => null), the standard safe-parse

Verification

  • biome check exits 0 across all 609 files
  • pnpm typecheck clean · 441 unit tests pass · i18n parity (4,763 keys) · pnpm audit --prod clean
  • Production build succeeds · full Playwright suite 41/41
  • pnpm build-storybook succeeds — the Babel compat canary called out in pnpm-workspace.yaml

🤖 Generated with Claude Code

…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>
@adamXbot
adamXbot requested a review from AdamXweb as a code owner July 28, 2026 11:59
@AdamXweb
AdamXweb merged commit c9da315 into main Jul 29, 2026
16 checks passed
@AdamXweb
AdamXweb deleted the chore/biome-2.5.5-rule-policy branch July 29, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants