fix(ci): make biome lint/format robust to the .claude worktree path#20
Conversation
`biome.json` excludes `**/.claude`. Claude Code worktrees live under `.claude/worktrees/<id>/`, so a bare `biome … .` run from a worktree self-excludes (the `.` arg resolves to a path under `.claude`) and silently processes ZERO files, exiting 0. That's exactly how an unformatted fixture sailed past local `pnpm format:check` and only blew up in CI (which runs from a normal checkout). Fix: the root lint / format / format:check scripts now pass explicit paths (`packages test-env *.json`) instead of `.`. Biome evaluates its `includes` patterns relative to biome.json, so those relative paths never match the `.claude` exclusion — the scripts process the same 62 files whether run from a worktree or the main checkout. Verified from a worktree: - `pnpm lint` → Checked 62 files, pass (was: 0 files) - `pnpm format:check` → Checked 62 files, pass (was: 0 files) - negative test: a stray blank line in a fixture now makes `pnpm format:check` exit 1 (previously exit 0). CLAUDE.md gotcha note updated to document the explicit-paths convention. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates the Biome linting and formatting scripts in package.json to target explicit paths (packages test-env *.json) instead of . to prevent Biome from self-excluding when run from a Claude Code worktree, and updates CLAUDE.md to document this behavior. The review feedback points out that the *.json glob pattern is not natively supported by Biome and will fail on Windows shells (like cmd.exe) that do not perform glob expansion. It is recommended to replace the wildcard with explicit filenames in both the package.json scripts and the CLAUDE.md documentation.
| "scripts": { | ||
| "build": "pnpm -r build", | ||
| "lint": "biome lint .", | ||
| "lint": "biome lint packages test-env *.json", |
There was a problem hiding this comment.
Biome does not natively support glob expansion (like *.json) in CLI arguments. On Windows environments using shells that do not perform glob expansion (such as cmd.exe), passing *.json literally to Biome will cause it to fail with an error. To ensure cross-platform compatibility, explicitly list the root JSON files instead of using a wildcard.
| "lint": "biome lint packages test-env *.json", | |
| "lint": "biome lint packages test-env package.json biome.json tsconfig.base.json", |
| "format": "biome format --write packages test-env *.json", | ||
| "format:check": "biome format packages test-env *.json" |
There was a problem hiding this comment.
Similarly, replace the *.json glob with explicit filenames to prevent failures on Windows shells (like cmd.exe) that do not perform glob expansion.
| "format": "biome format --write packages test-env *.json", | |
| "format:check": "biome format packages test-env *.json" | |
| "format": "biome format --write packages test-env package.json biome.json tsconfig.base.json", | |
| "format:check": "biome format packages test-env package.json biome.json tsconfig.base.json" |
| - **Default shell is zsh** on this laptop. `bash -c` scripts in tooling should stay POSIX-portable or be invoked under zsh explicitly. | ||
| - **`gh` auth** lives in the interactive zsh profile. If `gh` looks unauthenticated under a plain non-interactive shell, retry under `zsh -i -c '…'` — `GITHUB_TOKEN` is loaded from `.zshrc`. | ||
| - **Biome ignores `**/.claude`** in `biome.json`. The Claude Code worktree path lives under `.claude/worktrees/<id>/`, which means running `pnpm lint` from a worktree silently lints zero files. Lint per-package (`pnpm --filter <pkg> exec biome lint .`) from a worktree, or run the workspace lint from the main checkout. | ||
| - **Biome and the `.claude` worktree path.** `biome.json` excludes `**/.claude`, and Claude Code worktrees live under `.claude/worktrees/<id>/`. A bare `biome … .` therefore self-excludes (the `.` arg resolves under `.claude`) and silently processes **zero** files. To avoid that trap, the root `lint` / `format` / `format:check` scripts pass **explicit paths** (`packages test-env *.json`) instead of `.` — Biome evaluates `includes` relative to `biome.json`, so those relative paths never hit the `.claude` exclusion and the scripts work identically from a worktree or the main checkout. If you add a new top-level lintable directory, add it to those three scripts (otherwise it won't be checked). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 977d5e635e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "scripts": { | ||
| "build": "pnpm -r build", | ||
| "lint": "biome lint .", | ||
| "lint": "biome lint packages test-env *.json", |
There was a problem hiding this comment.
Avoid shell-only globs in package scripts
On Windows, pnpm scripts run under cmd.exe by default, which does not expand *.json; Biome also documents that “Glob patterns used on the command line are not interpreted by Biome” and are expanded by the shell (https://next.biomejs.dev/guides/configure-biome/#include-files-via-cli). In that environment this script passes a literal *.json path, so root JSON files are not linted and the command may fail instead of providing the intended cross-platform gate; the same pattern is used in the format scripts below.
Useful? React with 👍 / 👎.
Summary
Follow-up to #19. That PR fixed the unformatted fixture; this one fixes the root cause — the local format/lint gate was a silent no-op from a Claude Code worktree, so the regression was invisible until CI.
biome.jsonexcludes**/.claude. Claude Code worktrees live under.claude/worktrees/<id>/, sobiome … .run from a worktree resolves.to a path under.claude, self-excludes, and processes zero files while exiting 0.Fix: the root
lint/format/format:checkscripts pass explicit paths (packages test-env *.json) instead of.. Biome evaluatesincludesrelative tobiome.json, so those relative paths never match the.claudeexclusion. Same 62 files processed from a worktree or a clean checkout — local and CI can no longer diverge.Verification (from a worktree)
pnpm lintpnpm format:checkNegative test: a stray blank line in a fixture now makes
pnpm format:checkexit 1 (was exit 0) — the gate actually catches regressions again.Notes
*.jsoncovers the three root config files (package.json,biome.json,tsconfig.base.json);packagesandtest-envcover the rest. If a new top-level lintable directory is added, append it to these three scripts — documented in the updated CLAUDE.md gotcha note.!**/.claudeexclusion stays inbiome.json(still useful so a manualbiome lint .from the main checkout doesn't descend into sibling worktrees).🤖 Generated with Claude Code