Skip to content

chore(frontend): consolidate on Biome and drop oxlint/oxfmt [#10390] - #10399

Open
saltas888 wants to merge 3 commits into
developfrom
feat/linter-consolidation-9d44b6
Open

chore(frontend): consolidate on Biome and drop oxlint/oxfmt [#10390]#10399
saltas888 wants to merge 3 commits into
developfrom
feat/linter-consolidation-9d44b6

Conversation

@saltas888

@saltas888 saltas888 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Closes #10390. Supersedes #10393.

Why

The frontend supported two linters. frontend/app (1457 files) was gated by Biome in CI, frontend/packages/ui and frontend/packages/graph (81 files) shipped oxlint + oxfmt configs that nothing ever ran, so both packages had been unchecked since they were created (packages/ui 2026-03-18, packages/graph 2026-06-11). No ADR recorded the split, and because it was undocumented it actively misled: running biome check --write against a package file reformats it to whatever config Biome finds by walking up, which in a worktree is a different checkout entirely.

#10393 proposed enforcing each package's existing toolchain in CI. That fixes the coverage hole but keeps two toolchains, so this PR replaces it.

Why Biome rather than oxc

Measured both directions rather than guessing:

consolidate on Biome consolidate on oxc
files reformatted 28 (of 69 in the packages) 62 (of 1458 in the app)
lint config to port 3 noBarrelFile overrides ~100 ultracite rule decisions, no oxc equivalent
formatter maturity Biome 2.5.1, stable oxfmt 0.63, beta since Feb 2026, no 1.0
already invested app, CI, AGENTS.md, pre-ci, editor config nothing, zero enforcement to date

Biome wins on every axis, and a pre-1.0 formatter owning 1458 files means reformat churn on each bump. Worth noting the submodule packages/schema-visualizer is already on Biome too, so oxc was the only outlier.

The concern I expected to block this did not: Biome's Tailwind class sorting is a WIP nursery rule with no custom-utility support, while packages/ui pointed oxfmt at a Tailwind v4 stylesheet. But useSortedClasses is already enabled via ultracite/biome/core and the app is clean on all 1457 files, so it is in force and satisfied today. The design system gets lower-fidelity ordering, not none. oxfmt's sortPackageJson is genuinely lost.

What changed

Biome now owns the whole frontend/ pnpm workspace, using Biome 2's monorepo layout:

Config Role
frontend/biome.jsonc root: shared formatter, import sorting, lint rules, ultracite presets
frontend/app/biome.jsonc app-relative ignores (generated GraphQL/REST types, Playwright output)
frontend/packages/{ui,graph}/biome.jsonc noBarrelFile off, src/index.ts is each package's public API

The per-package configs are load-bearing, not decoration: without them the packages are not Biome projects, and noUndeclaredDependencies cannot resolve their own package.json, reporting all 139 imports as undeclared.

  • CI's frontend-lint job runs one biome ci . from ./frontend instead of ./frontend/app, so the app and the packages are gated together. No install change, pnpm install --frozen-lockfile already installs the whole workspace.
  • Biome and ultracite moved to the workspace root package, so there is a single entry point, cd frontend && pnpm biome:fix. The member-level biome scripts were removed on purpose, invoking Biome from frontend/app silently leaves packages/* unchecked, which is exactly how the split went unnoticed.
  • packages/plugins/template is excluded, it is a standalone published scaffold on its own npm + ESLint setup. packages/schema-visualizer is excluded as a submodule with its own config.
  • The four ox*.config.ts files are deleted. The two oxlint-disable-next-line directives became plain rationale comments, since both rules they suppressed are off under Biome and a biome-ignore would be an unused suppression.
  • oxfmt is gone from the lockfile entirely, 40 package entries removed. oxlint survives only as a transitive of react-scan to react-doctor, nothing we own configures or runs it.

Also updated so nobody runs Biome from the wrong directory: AGENTS.md, frontend/app/AGENTS.md, .agents/commands/pre-ci.md, dev/knowledge/frontend/design-system.md (which now documents the toolchain, the gap #10390 called out), dev/setup-environment.sh, and the four bug-agent workflow/doc invocation sites.

Reformatting risk

Enforcing Biome on previously unchecked code reformatted 28 files and reordered Tailwind classes in 81 places. Class order inside a className is a no-op for rendering, but a dropped class would not be, so I audited the diff token-by-token: every Tailwind utility in a removed line reappears in the corresponding added line. The only tokens unique to either side are trailing commas from re-wrapping and the two rewritten comments. No class was added or dropped.

Base branch

Targeting develop, not stable. Of the 42 files this rewrites under frontend/packages/, 33 are also modified on develop relative to stable, and whole-file reformats against content changes in the same regions do not auto-merge. Five shared files it edits also differ between the branches (app/package.json, pnpm-lock.yaml, ci.yml, app/AGENTS.md, design-system.md). release-1.11 is live, and dev/guidelines/git-workflow.md forbids merging stable forward into an older release branch, so a stable base would need cherry-picking there too. This is tooling debt, not a bug in released code.

Test plan

Every gate run locally, all green:

Gate Result
cd frontend && pnpm exec biome ci . clean, 1542 files
cd frontend/app && pnpm knip exit 0
cd frontend/app && pnpm exec betterer ci 186 issues, unchanged
cd frontend/app && pnpm test 185 files, 1241 tests pass
cd frontend/packages/graph && pnpm test 5 files, 23 tests pass
cd frontend/packages/graph && tsc -b clean
yamllint -s on the changed workflows clean
pnpm install --frozen-lockfile exit 0

The one thing local runs cannot cover is Chromatic on packages/ui. Class reordering is semantically inert and the component tests pass, but a visual diff would make that certain.

🤖 Generated with Claude Code

Review in cubic

frontend/packages/ui and frontend/packages/graph shipped oxlint + oxfmt
configs that CI never ran, so both packages had gone unchecked since they
were created, while frontend/app was gated by Biome. Two toolchains, one
of them enforced.

Biome now owns the whole frontend/ pnpm workspace. A root config at
frontend/biome.jsonc holds the shared formatter, import sorting and lint
rules; thin per-member configs inherit it via "extends": "//". Those
per-package configs are what make each package its own Biome project --
without them noUndeclaredDependencies cannot resolve the package's own
package.json and reports every import as undeclared. CI's frontend-lint
job now runs a single `biome ci .` from the workspace root, covering the
app and the packages together.

Enforcing Biome on the previously unchecked packages reformatted 28 files
and reordered Tailwind classes in 81 places. Class order inside a
className is a no-op for rendering, and a token audit of the diff
confirms no class was added or dropped. The two oxlint-disable directives
became plain rationale comments: both rules they suppressed are off under
Biome, so a biome-ignore would be an unused suppression.

oxfmt is gone from the lockfile entirely (40 package entries removed).
oxlint survives only as a transitive dependency of react-scan ->
react-doctor; nothing we own configures or runs it.

Biome is a devDependency of the workspace root so there is one entry
point, `cd frontend && pnpm biome:fix`. The member-level biome scripts
were removed deliberately -- invoking Biome from frontend/app silently
leaves packages/* unchecked, which is how the split went unnoticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@saltas888 saltas888 added group/frontend Issue related to the frontend (React) group/ci Issue related to the CI pipeline type/tech-debt Item we know we need to improve way it is implemented labels Aug 25, 2026
@codspeed-hq

codspeed-hq Bot commented Aug 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 13 untouched benchmarks


Comparing feat/linter-consolidation-9d44b6 (9146609) with develop (bf23d09)

Open in CodSpeed

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

22 issues found across 78 files

Confidence score: 3/5

  • In frontend/biome.jsonc, the files.includes arrays use only negated ! patterns without a leading positive **. Biome 2 docs require ** first for negated patterns to match anything, so this could cause Biome to ignore the intended files, leaving code unformatted or breaking CI lint checks — add a leading ** pattern.
  • This PR is a pure tooling/CI/docs reorganization (Biome consolidation, no product behavior) but targets develop across many files (e.g., .github/workflows/claude-code.yml, frontend/app/AGENTS.md, frontend/packages/ui/src/components/checkbox/checkbox.tsx). Per the base-branch policy these should land on stable; retarget or split the PR to avoid policy violations, and update frontend/app/README.md which still references the removed biome:fix script.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="changelog/+frontend-biome-consolidation.housekeeping.md">

<violation number="1" location="changelog/+frontend-biome-consolidation.housekeeping.md:1">
P3: This is a pure tooling/CI/documentation change (no product code, no user-visible behavior), but the PR targets develop. Per the release-vehicle policy, repo tooling and CI changes should target stable since they cannot affect a running product and should ship before the next minor. Consider retargeting this consolidation to stable.</violation>
</file>

<file name="frontend/packages/ui/src/components/checkbox-card/checkbox-card.tsx">

<violation number="1" location="frontend/packages/ui/src/components/checkbox-card/checkbox-card.tsx:16">
P3: Per the base-branch policy, a pure tooling/CI refactor (Biome consolidation, no product behavior change) should target `stable`, not `develop`, since it cannot affect a running product. This is a low-severity note, not blocking.</violation>
</file>

<file name=".github/workflows/bug-agent-fix.md">

<violation number="1" location=".github/workflows/bug-agent-fix.md:205">
P3: This is a pure tooling/docs/CI change (frontend Biome invocation in the bug-fix workflow) and the PR targets develop. Per the repo's base-branch policy, repo tooling changes belong on stable; consider retargeting to stable so this ships outside the feature release train, or confirm the develop target is intentional.</violation>
</file>

<file name="frontend/app/AGENTS.md">

<violation number="1" location="frontend/app/AGENTS.md:17">
P3: This is a pure tooling/docs (AGENTS.md) change, which per the repo's base-branch policy should land on `stable` since it can't affect a running product. The PR targets `develop`, so move the docs (and the rest of this tooling consolidate) to `stable` to avoid shipping a versionless docs/CI change on the product release train.</violation>
</file>

<file name="frontend/packages/ui/src/components/checkbox/checkbox.tsx">

<violation number="1" location="frontend/packages/ui/src/components/checkbox/checkbox.tsx:14">
P3: This PR is a pure tooling/CI consolidation (Biome replacing oxlint/oxfmt; no product behavior change — class order is a no-op for rendering). Per the base-branch convention, changes that can't affect a running product (repo tooling, CI, docs) belong on stable, while develop is reserved for product changes on the normal release train. Consider targeting stable rather than develop. Non-blocking note.</violation>
</file>

<file name=".agents/commands/pre-ci.md">

<violation number="1" location=".agents/commands/pre-ci.md:47">
P3: This is a pure developer-tooling/docs change (repointing the Biome command in pre-ci.md), which per the release-vehicle policy belongs on stable, not develop. The PR targets develop; note that this splits the tooling migration from the release train it should ride, and lower-severity tooling/docs/CI work shipping on develop will only reach users on the next minor.</violation>
</file>

<file name=".github/workflows/claude-code.yml">

<violation number="1" location=".github/workflows/claude-code.yml:140">
P3: This PR is a pure tooling/CI/docs reorganization and targets `develop`. Per the base-branch convention, repo tooling/CI changes should target `stable` (since they cannot affect the running product) rather than ride the normal release train on `develop`; product features/fixes are what belong on `develop`. Non-blocking note only.</violation>
</file>

<file name="frontend/packages/ui/src/components/breadcrumbs/breadcrumbs.tsx">

<violation number="1" location="frontend/packages/ui/src/components/breadcrumbs/breadcrumbs.tsx:34">
P3: This is a no-op formatting change (Tailwind class reorder and trailing-comma removal) with zero impact on the running product, but the PR targets develop. Per the base-branch selection rule, changes that cannot affect a running product belong on stable, and only product changes ride the develop release train. Consider pointing this reformatting onto stable to avoid shipping a source-only churn on the normal release path.</violation>
</file>

<file name="AGENTS.md">

<violation number="1" location="AGENTS.md:97">
P3: This PR is pure repo tooling/docs/CI (migrating frontend linting to Biome) and, per the repo's base-branch convention, should target `stable` rather than `develop`, which is reserved for the normal product release train. Tooling/doc/CI changes don't affect a running product and can ship ahead of the next minor.</violation>
</file>

<file name="frontend/packages/graph/package.json">

<violation number="1" location="frontend/packages/graph/package.json:22">
P3: This is a pure repo-tooling change (removing lint/format scripts and toolchain devDependencies), which per the base-branch policy belongs on stable rather than develop. Nothing in the diff affects a running product. Move/cherry-pick this to stable, or confirm the develop targeting is deliberate.</violation>
</file>

<file name="frontend/packages/ui/src/components/breadcrumbs/breadcrumbs.stories.tsx">

<violation number="1" location="frontend/packages/ui/src/components/breadcrumbs/breadcrumbs.stories.tsx:25">
P3: This is a pure tooling/formatting change (Biome migration + reformatting) that cannot affect a running product, but it targets develop. Per the release-vehicle rule, repo tooling/formatting should target stable; consider retargeting the branch (or noting the deliberate exception) so the formatting churn stays off the product release train.</violation>
</file>

<file name=".github/workflows/bug-agent-test.md">

<violation number="1" location=".github/workflows/bug-agent-test.md:259">
P3: This PR is a pure tooling/docs/CI change (Biome/ultracite consolidation, config and workflow updates, AGENTS.md and docs, 28 reformatted frontend files with no functional change) and per repo convention should target stable, since repo tooling can't affect a running product. It currently targets develop; this is a low-severity note, not blocking.</violation>
</file>

<file name="frontend/app/biome.jsonc">

<violation number="1" location="frontend/app/biome.jsonc:2">
P3: This is a pure repo-tooling/docs/CI change (Biome/radoxlint consolidation with no runtime effect) and is targeting develop rather than stable. Per the base-branch convention, changes that can't affect a running product should ship on stable; targeting develop puts tooling churn on the product release train and creates the cherry-pick risk already noted in the description. Low-severity note, not blocking.</violation>
</file>

<file name="frontend/package.json">

<violation number="1" location="frontend/package.json:8">
P3: This PR is a pure tooling/dev-dependency change (consolidating frontend tooling on Biome, removing oxfmt/oxlint), which targets develop. Per the base-branch convention, pure tooling/docs/CI changes should target stable since they cannot affect a running product and can ship before the next minor. Low severity, non-blocking.</violation>

<violation number="2" location="frontend/package.json:10">
P3: The `biome:fix` script was moved from `frontend/app` to the workspace root, but `frontend/app/README.md` still tells users to run `pnpm biome:fix` inside the app, where the script no longer exists and the command now fails. Update the app README to `cd frontend && pnpm biome:fix` like AGENTS.md.</violation>
</file>

<file name="frontend/packages/graph/src/components/export-menu/export-menu.tsx">

<violation number="1" location="frontend/packages/graph/src/components/export-menu/export-menu.tsx:73">
P3: This change is repo tooling/CI/docs consolidation (Biome migration plus cosmetic reformatting with no behavioral impact) and is targeted at develop. Per the release-vehicle policy, tooling/docs/CI changes that can't affect the running product should target stable rather than the normal-develop release train. Low-severity note only, not blocking.</violation>
</file>

<file name="frontend/app/package.json">

<violation number="1" location="frontend/app/package.json:21">
P3: Removing the `biome:fix` script from `frontend/app/package.json` leaves `frontend/app/README.md` (line 12) still documenting `pnpm biome:fix` as an app-local command. A developer following the app README from `frontend/app` now gets "Command 'biome:fix' not found". Update the README to the root command `cd frontend && pnpm biome:fix` (as AGENTS.md already does).</violation>

<violation number="2" location="frontend/app/package.json:21">
P3: This is a pure repo-tooling/CI change, which per your base-branch convention belongs on `stable` (changes that can't affect a running product), yet the PR description states "Targeting develop (not stable)". Note intended as informational, not blocking.</violation>
</file>

<file name="frontend/packages/ui/src/components/sortable-list/sortable-list.stories.tsx">

<violation number="1" location="frontend/packages/ui/src/components/sortable-list/sortable-list.stories.tsx:49">
P3: Per the release-vehicle convention, a pure tooling/docs/CI change should target stable, not develop. This PR consolidates frontend tooling onto Biome (config, CI gates, script removal) and, per the PR description, targets develop. It is a no-op for the running product (only formatting/class-order changes), so it belongs on the stable release vehicle rather than the feature train.</violation>
</file>

<file name="dev/knowledge/frontend/design-system.md">

<violation number="1" location="dev/knowledge/frontend/design-system.md:101">
P3: This is a pure tooling/docs change (Biome consolidation plus developer docs) that cannot affect a running product, so per the base-branch policy it belongs on stable, not develop. The PR description states it targets develop. Move it (or the doc-only portions) to stable, or confirm the tooling change intends to ship as a product change.</violation>
</file>

<file name="frontend/packages/graph/src/index.ts">

<violation number="1" location="frontend/packages/graph/src/index.ts:3">
P3: This is a pure tooling/formatting/CI consolidation (Biome migration) placed on the develop release train. Per release-vehicle policy, repo tooling changes that cannot affect a running product belong on stable; confirm this isn't accidentally queued for the next minor. If develop is required (the description notes frontend/packages diverge between branches and cherry-picking is a concern), that's acceptable — this is a low-severity, non-blocking note.</violation>
</file>

<file name="frontend/biome.jsonc">

<violation number="1" location="frontend/biome.jsonc:11">
P1: Each `files.includes` array in this PR (root, app, packages/ui, packages/graph) contains only negated `!` patterns with no leading positive `**`. Biome 2 docs require `**` first for negated patterns to match anything, and I verified with Biome 2.5.1 that a config whose `includes` is only negated patterns processes zero source files (only the config file itself). As written, `cd frontend && pnpm biome ci .` would silently lint/format no source files, defeating the unified gate this PR introduces. Add `"**"` as the first entry in each `files.includes` array.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread frontend/biome.jsonc
Comment thread changelog/+frontend-biome-consolidation.housekeeping.md
Comment thread frontend/packages/ui/src/components/checkbox-card/checkbox-card.tsx
Comment thread .github/workflows/bug-agent-fix.md
Comment thread frontend/app/AGENTS.md
Comment thread frontend/app/package.json
Comment thread dev/knowledge/frontend/design-system.md
Comment thread frontend/packages/graph/src/index.ts
Comment thread frontend/package.json
…0390]

Three follow-ups on #10399:

- Reword the changelog fragment; vale's release-notes style rejects
  "config" (wants "configuration") and flags "CI's" as a spelling error.
- frontend/app/README.md still documented `pnpm biome:fix` as an
  app-local script after the script moved to the workspace root
  (caught by review). Point it at `pnpm -w biome:fix`.
- Exclude src/shared/api/errors/catalogue.generated.ts in the app's
  Biome config alongside the other generated artefacts. It was already
  skipped -- ultracite's preset force-excludes `!!**/*.generated.*` --
  but the explicit entry documents the intent next to its two generated
  siblings instead of relying on the preset.

Review also claimed the negated-only files.includes arrays make Biome
check zero files and prescribed adding "**". Both halves are wrong
here: the arrays merge onto ultracite's preset, which already supplies
the "**" catch-all (the gate demonstrably checks 1542 files and the
negations were verified to exclude schema-visualizer and plugins), and
adding our own "**" trips Biome's noBiomeFirstException error while
accidentally re-including generated files the preset deliberately
excludes. Left as-is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files (changes from recent commits).

Confidence score: 4/5

  • frontend/app/README.md targets develop even though this repository’s convention routes tooling and documentation changes to stable; the change could be delayed or follow the wrong release path—retarget the PR to stable.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/app/README.md">

<violation number="1" location="frontend/app/README.md:12">
P3: This is a pure tooling/docs/CI change targeting `develop`. Per this repo's base-branch convention, repo tooling and docs changes go to `stable` (so they ship without waiting for the next minor); `develop` is reserved for product changes on the normal release train. Low-severity, non-blocking note.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread frontend/app/README.md
@saltas888
saltas888 marked this pull request as ready for review August 25, 2026 12:16
@saltas888
saltas888 requested a review from a team as a code owner August 25, 2026 12:16
The frontend workspace is pnpm-managed, but pre-ci.md and the
claude-code workflow allowlist still invoked biome, betterer and
codegen via npx/npm run. Switch them to the pnpm equivalents
(pnpm biome:fix, pnpm exec betterer, pnpm codegen:graphql).
npx markdownlint stays: docs/ is npm-managed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 4 unresolved issues from previous reviews.

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group/ci Issue related to the CI pipeline group/frontend Issue related to the frontend (React) type/tech-debt Item we know we need to improve way it is implemented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant