chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore: introduce Biome linter with CI gate (#415) - #1019

Merged
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome
Jul 15, 2026
Merged

chore: introduce Biome linter with CI gate (#415)#1019
Astro-Han merged 4 commits into
apache:mainfrom
UncertaintyDeterminesYou4ndMe:chore/415-introduce-biome

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
Contributor

Closes part of #415 (config + scripts + CI gate; the full-repo auto-fix is a dedicated follow-up PR per the issue's own sequencing).

What

  • Install @biomejs/biome2.5.4 (exact-pinned, root devDependency).
  • Add biome.jsonc: formatter matched to the existing dominant style (2-space, 100-col, single quotes, semicolons, trailing commas), linter from the recommended preset, VCS-aware file discovery (useIgnoreFile), and Tailwind CSS 4 directive parsing (the desktop renderer stylesheet uses @import/@source, which the default CSS parser rejects).
  • Root scripts: npm run lint (biome lint .) and npm run format (biome format --write .).
  • CI: npm run lint added to the typecheck job right after npm ci — fast-fail, needs no build.

Zero source diffs, by design

This PR touches exactly 4 files: package.json, package-lock.json, biome.jsonc, .github/workflows/ci.yml. No source file is reformatted or lint-fixed, so it can't conflict with the currently open PRs.

To make the gate pass on the current tree, 64 recommended rules with pre-existing violations are disabled, each annotated in biome.jsonc with its violation count. Top offenders: noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies (110), noAdjacentSpacesInRegex (84), noUnusedImports (74), noUnsafeOptionalChaining (74), noExplicitAny (60). The gate still enforces everything else in the recommended preset from day one, and locks in a ratchet: new violations of the enabled rules fail CI.

biome format . (check-only) reports 1299/1555 files would be reformatted — deferred.

Follow-up (next PR, on request)

  1. Run the auto-fixable disabled rules + formatter + import sorting in one mechanical commit.
  2. Re-enable rules as their violation counts hit zero; hand-fix the small-count correctness rules (noDuplicateObjectKeys, noUnsafeOptionalChaining, …).

Verification

  • npm run lint → exit 0 (Checked 1583 files in ~0.5s).
  • npm run typecheck on this branch is unchanged from its merge-base behavior (verified by stash/rerun); the CI job builds before typechecking as before.

Installs @biomejs/biome 2.5.4 (exact) and adds biome.jsonc: formatter
(2-space, 100-col, single quotes, semicolons, trailing commas), linter
starting from the recommended preset, VCS-aware file discovery, and
Tailwind CSS 4 directive parsing for apps/desktop's renderer stylesheet.
Adds `npm run lint` (biome lint .) and `npm run format` (biome format
--write .) scripts, and wires `npm run lint` into the CI typecheck job
right after `npm ci` for a fast-fail gate.
Import-sorting/organize-imports assist actions are left disabled
(assist.enabled: false) — that lands in a follow-up auto-fix PR per apache#415.
64 recommended lint rules had pre-existing violations on this tree and
are disabled (each with its violation count as a comment) so `npm run
lint` passes without touching any source file. Top offenders:
noNonNullAssertion (1274), useTemplate (137), useExhaustiveDependencies
(110), noAdjacentSpacesInRegex (84), noUnusedImports (74),
noUnsafeOptionalChaining (74), noControlCharactersInRegex (67),
noExplicitAny (60), noLabelWithoutControl (51).
`npx biome format .` (check-only) reports 1299 of 1555 scanned files
would be reformatted — deferred to the follow-up auto-fix PR, not
applied here.
@Astro-Han

Astro-Han commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The gate is wired correctly (runs on push/PR to main, after npm ci, before build), the config is valid for Biome 2.5.4, the version is pinned exactly, and the ratchet does fail CI on a new violation of an enabled rule. One thing to fix before merge, then a recommendation on the shape.

Must fix: the 9 new @biomejs/* lockfile entries resolve to registry.npmmirror.com while every other dependency uses registry.npmjs.org (main has zero npmmirror URLs). With no tracked .npmrc, CI npm ci will honor these URLs and fetch Biome from the mirror, which is slower and can be rate-limited or unreachable from GitHub runners. Regenerate just these entries against npmjs (npm install @biomejs/biome@2.5.4 --registry=https://registry.npmjs.org/) and commit the corrected lockfile.

Recommendation on the shape, worth reconsidering before this becomes the baseline. Measured against the full recommended set, the tree has ~2,300 diagnostics, but 57% is one pure-style rule (noNonNullAssertion = 1,274) and the bulk of the rest is style (useTemplate 137, useExhaustiveDependencies 110, noExplicitAny 60). The genuine correctness debt is about 76 and almost entirely in test files, and it includes one real masked bug: scripts/cu-provider-matrix.test.mjs:89 has a duplicate fixtureState key that line 91 silently overrides, so the test does not exercise its intended input. noDuplicateObjectKeys catches exactly this, and the PR disables it.

So the current shape enforces a lint gate that is green only because the bug-catchers are off, while the formatter is configured but never gated (zero consistency value). Consider inverting it:

  • Lint: enforce only the correctness subset (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain, and similar). Hand-fix the ~76 (mostly tests; fix the duplicate-key bug by hand rather than autofix, which would just delete one key). Drop the pure-style rules outright instead of tracking 64 as debt. useExhaustiveDependencies is a real bug-catcher but needs per-case judgment, so leave it to a follow-up.
  • Formatter: land a one-time biome format --write baseline (1,299 files, mechanical) as a standalone commit and gate it with biome format in check mode, or drop the formatter config. Configured-but-ungated buys nothing. If baselining, add the commit to .git-blame-ignore-revs and coordinate timing against the open PRs, since it touches ~1,300 files.

陆逊 added 2 commits July 15, 2026 14:18
…e#415)
The local npm config points at a npmmirror.com mirror, which leaked 9
resolved URLs for @biomejs/* packages into package-lock.json; repin
them to the canonical registry.npmjs.org tarball URLs.
…pache#415)
Reshape the gate per PR apache#1019 review: correctness-first instead of
tracking style debt.
- cu-provider-matrix.test.mjs: remove the stale flat fixtureState
duplicate key that silently overrode the windowId-keyed shape the
test actually exercises (the masked bug noDuplicateObjectKeys
catches).
- Hand-fix all noUnsafeOptionalChaining sites (74) and the one
noNonNullAssertedOptionalChain site across 12 test files: drop `?.`
where presence is already asserted/constructed, extend the chain
where undefined still fails the assertion identically, or use a
non-null assertion in TS tests where presence is structural.
- Re-enable noDuplicateObjectKeys, noUnsafeOptionalChaining, and
noNonNullAssertedOptionalChain in the gate.
- biome.jsonc: reorganize remaining off-rules into "dropped outright"
(noNonNullAssertion, useTemplate, noExplicitAny — pure style, not
tracked as debt) vs "deferred" (follow-up fix passes);
useExhaustiveDependencies noted as a real bug-catcher needing
per-case judgment.
Verified: biome lint clean; test:scripts 91/91; runtime 1936 tests
0 fail; desktop 2542/2542; typecheck clean. headless 960/963 — the
2 failures (harbor-adapter opencode stop runner) reproduce identically
on the unmodified baseline and pass in isolation; local-only flake.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review — both points addressed, plus measurement data that informs the formatter question.

Fixed

Lockfile registry (f34d435f): the 9 @biomejs/* entries were leakage from a local npmmirror config; regenerated against registry.npmjs.org, grep -c npmmirror package-lock.json is now 0.

Correctness-first reshape (ef9102e5), as you suggested:

  • The fixtureState duplicate key in cu-provider-matrix.test.mjs is fixed by hand: the flat { blue, red, note } shape is the stale one — normalizeFixture consumes the windowId-keyed shape ({ target: {…} }) and every other test in the file uses it, so the flat entry was dead input, now removed.
  • All 74 noUnsafeOptionalChaining sites + the 1 noNonNullAssertedOptionalChain site hand-fixed across 12 test files (drop ?. where presence is already asserted; extend the chain where an undefined still fails the assertion identically; non-null assertion in TS tests where presence is structural).
  • noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain are now enforced.
  • noNonNullAssertion / useTemplate / noExplicitAny are dropped outright (commented as deliberate, not debt). The rest stay off as "deferred" with counts; useExhaustiveDependencies annotated as a real bug-catcher needing per-case judgment.

Verified locally: lint clean, test:scripts 91/91, runtime 1936 tests 0 fail, desktop 2542/2542, typecheck clean. headless is 960/963 — the 2 failures (harbor-adapter opencode stop runner) reproduce identically on the unmodified baseline and pass in isolation, so they're a local-env flake, not from this diff; CI is the arbiter there.

Formatter: measurement first, then your call

I measured auto-fix coverage on a throwaway worktree with all 64 rules re-enabled (2,441 baseline diagnostics):

passdiagnostics clearedfiles touched
lint --write (safe)157 (6.4%), 11 rules to zero81
+ --unsafe764 (31.3%) cumulative, 21 rules to zero233
format --write alone1,299 (0.4s)

Two findings worth knowing before anyone runs a real fix pass:

  1. Unsafe fixes shift violations: stripping ! for noNonNullAssertion pushed noNonNullAssertedOptionalChain from 1 → 12. Unsafe passes need a build+test gate, not just a lint-clean check.
  2. biome lint --write --unsafe hangs indefinitely (~100% CPU) on apps/desktop — every other directory completes in ~5s, and plain format --write is fine everywhere, so it's specific to the unsafe-fix lint pass on that tree. Needs bisection before an unsafe pass runs there; I'll try to minimize it and file it upstream with Biome.

On your two options: I'd take the one-time format baseline + biome format check-mode gate, as a standalone PR you merge at a moment of your choosing (it conflicts with everything open, so timing is yours), with the commit added to .git-blame-ignore-revs. Happy to prep that PR now so it's one click when you want it — or drop the formatter config from this PR if you'd rather not carry an ungated config in the meantime. Which do you prefer?

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks, the reshape is exactly right and CI is green. Two things before merge.

Formatter: let's go with (b). Drop the formatter config (and the npm run format script) from this PR so it lands as a clean correctness-lint gate. Bring the formatter back as one atomic follow-up: config plus the one-time biome format --write baseline plus a biome format check-mode CI gate plus the baseline commit in .git-blame-ignore-revs, timed when few PRs are open. Reason not to keep it here: formatter.enabled: true on main gets picked up by format-on-save in Biome-enabled editors, so the 1,299-file reformat leaks piecemeal into unrelated PRs, and with no baseline commit .git-blame-ignore-revs can't undo scattered drift. I'll take you up on the offer to prep that baseline PR for the follow-up.

Linter shape: make it an explicit allowlist rather than recommended minus the disabled rules. Set recommended: false and turn on only the three correctness rules you enforce (noDuplicateObjectKeys, noUnsafeOptionalChaining, noNonNullAssertedOptionalChain). Same green result today, but it inverts the default so nothing re-enters the gate silently, and it drops the 64-rule disabled list entirely instead of carrying it as standing debt. The thing this guards against is ruleset creep: with recommended on, someone later flips a style rule back on (the 1,274 noNonNullAssertion sites are right there) and the gate quietly turns from a correctness gate into a style gate. Add a short comment at the rules block fixing the admission bar, so future additions are a deliberate decision: a rule joins the gate only if it catches a defect rather than a style preference, and the tree is either zero-violation or hand-fixed first. Worth noting there too: skip --unsafe autofix for now, since biome lint --write --unsafe hangs on apps/desktop.

Happy to merge once the formatter config is out and the linter is on the explicit allowlist.

… formatter (apache#415)
Per PR apache#1019 review, address the two pre-merge asks.
Linter: invert from `recommended` minus a 64-rule disabled list to an
explicit allowlist (`preset: "none"`) that turns on only the three
correctness rules actually enforced — noUnsafeOptionalChaining,
noDuplicateObjectKeys, noNonNullAssertedOptionalChain. Nothing re-enters
the gate implicitly, so it can't silently drift from a correctness gate
into a style gate, and the standing 64-rule debt list is gone. A comment
fixes the admission bar: a rule joins only if it catches a defect (not a
style preference) and the tree is zero-violation or hand-fixed first.
Formatter: drop the formatter config and the `npm run format` script.
`formatter.enabled: true` on main is picked up by editors' format-on-save
and would leak the 1,299-file reformat piecemeal into unrelated PRs. It
returns as one atomic follow-up PR (config + one-time baseline + a
`biome format` check-mode gate + `.git-blame-ignore-revs`), timed when
few PRs are open.
Verified: `npm run lint` clean (exit 0, no deprecation notice); a probe
file confirms all three rules fire (negative test), so the gate is not a
silent no-op.
@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown
ContributorAuthor

Both pre-merge asks done in de4f00f1.

Formatter removed. Dropped the formatter config and the npm run format script — this PR is now purely a correctness-lint gate. Agreed on the format-on-save leakage risk; it comes back as the atomic baseline follow-up (config + one-time biome format --write + biome format check-mode gate + .git-blame-ignore-revs), which I'll prep and hand you to merge when few PRs are open.

Linter is now an explicit allowlist.preset: "none" (the non-deprecated spelling — recommended: false emits a deprecation notice in 2.5.4), turning on only the three enforced correctness rules. The 64-rule disabled list is gone entirely. Added the admission-bar comment you asked for: a rule joins only if it catches a defect rather than a style preference, and the tree must be zero-violation or hand-fixed first. Note in there too that --unsafe autofix is off the table for now given the apps/desktop hang.

Two verification notes:

  • npm run lint is clean with no deprecation output (exit 0).
  • Guarded against the "silent no-op gate" failure mode with a negative test: a throwaway probe file with all three violations trips all three rules (Found 3 errors), then removed. So the allowlist is genuinely active, not accidentally empty.

Net diff of this commit: biome.jsonc −99/+32, package.json −1. CI re-running.

@Astro-Han
Astro-Han merged commit d6d3573 into apache:mainJul 15, 2026
3 checks passed
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 16, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
UncertaintyDeterminesYou4ndMe pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit to UncertaintyDeterminesYou4ndMe/maka-agent that referenced this pull request Jul 19, 2026
…che#415)
Follow-up to apache#1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
likun666661 pushed a commit that referenced this pull request Jul 19, 2026
…op) (#415) (#1122)
* build(lint): re-enable Biome formatter with a check-mode CI gate (#415)
Follow-up to #1019, which introduced the linter but deliberately left the
formatter out. Restores the formatter config (2-space, 100-col, single
quotes, semicolons, trailing commas — matching dominant existing style),
adds `format` (write) / `format:check` scripts, and a `npm run
format:check` CI step in the typecheck job.
Formatter-scoped exclusions (all still fully linted):
- apps/desktop/** and packages/ui/**: the UI surface's source-contract
tests read .ts/.tsx source (desktop's own and packages/ui's) and
regex-match exact single-line shapes; a whole-tree reformat broke 145
desktop-source assertions plus ~45 contracts introspecting packages/ui.
UI-surface formatting lands as a follow-up once those contract tests
are format-tolerant.
- Byte-sensitive provenance artifacts (SOURCE.json is SHA-256'd and
size-checked by scripts/prepare-cua-driver.mjs) and bundled-tools.json.
The mechanical baseline reformat is the next commit; the check gate goes
green only with both included.
* style: apply Biome format baseline outside the UI surface (#415)
Regenerated on top of current main (the previous baseline commit was 90
commits stale and conflicting; a whole-repo mechanical reformat is
rebased by re-running the formatter, not by resolving 660 files of
textual conflicts). Same scope as before: biome.json excludes the
apps/desktop UI surface.
Two adjustments the reformat itself required: a @ts-expect-error in
proxy-test.ts moved onto the property line it suppresses (the reformat
split the fetch call, displacing the directive), and a second formatter
pass over two files where Biome's first pass is not idempotent.
* chore: add .git-blame-ignore-revs scaffold for the format baseline (#415)
* test(runtime): make RunTracePhase contract regex layout-tolerant (#415)
---------
Co-authored-by: 陆逊 <jiaxiong.liu@longbridge-inc.com>
@UncertaintyDeterminesYou4ndMe
UncertaintyDeterminesYou4ndMe deleted the chore/415-introduce-biome branch July 19, 2026 06:39
Sign up for freeto 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

@UncertaintyDeterminesYou4ndMe@Astro-Han