Skip to content

chore(tooling): exclude nested Claude Code worktrees from local quality gates - #262

Open
Dmitrii Ostasevich (kwinto) wants to merge 1 commit into
mainfrom
claude/elastic-hermann-838d30
Open

Dmitrii Ostasevich (kwinto) wants to merge 1 commit into
mainfrom
claude/elastic-hermann-838d30

Conversation

@kwinto

@kwinto Dmitrii Ostasevich (kwinto) commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Nested git worktrees under .claude/worktrees/<name> are full checkouts of this repo, each with its own src/, test/ and node_modules. The local quality gates have no way to know they aren't part of the tree under inspection, so with stray worktrees on disk they produce large numbers of failures that CI never sees (CI does a fresh checkout with no nested worktrees).

Observed with three stray worktrees present:

  • npm run lint:a11y → 42 errors, 10 warnings, every one of them from a file inside a nested worktree. Zero from src/ or test/.
  • npm test → collected the nested specs on top of the real suite. Those worktrees' node_modules supply a second React copy, so every collected rendering spec died with Cannot read properties of null (reading 'useContext') (Audio, Avatar, Datepicker, DatepickerA11y, Gallery, List, Text, Video, a11y.spec × N worktrees). Zero real failures.

Git already skips these paths, but only via .git/info/exclude — local to one clone, and no linter reads it. This PR moves the exclusion into the repo's own config so it travels with the checkout.

What changed

File Change
eslint.a11y.config.js ".claude/worktrees/**" added to ignores
eslint.config.js same pattern added to ignores
vite.config.ts "**/.claude/worktrees/**" added to test.exclude; array reflowed so each pattern carries its own comment
vitest.dom-compat.config.ts exclude changed from configDefaults.exclude to [...configDefaults.exclude, "**/.claude/worktrees/**"]
.prettierignore .claude/worktrees/ added
.gitignore .claude/worktrees/ added under the existing "Claude Code worktrees" block

Two deliberate choices worth a reviewer's attention:

  • Scoped to worktrees/, not all of .claude/**. The tracked .claude/ content (agents/, commands/, skills/wcag-component/) stays in scope exactly as it is on CI, so a future .ts/.tsx added under a skill is still linted locally and on CI alike. Ignoring all of .claude/ would have created the same local↔CI divergence this PR is fixing, just in the other direction.
  • The .gitignore line is included even though .git/info/exclude already covers it locally. Without it, a teammate who creates a nested worktree sees it as untracked in git status, and prettier --check . picks it up (Prettier 3 reads .gitignore by default). One-line addition in the block that already says "Claude Code worktrees".

The dom-compat side is worth calling out: the existing test/dom-compat.spec.tsx exclude is root-anchored, so it does not match a nested worktree's copy — dom-compat.spec.tsx was leaking into the default npm test run and failing on an unresolvable ../dist/chat-components.js import. The new pattern closes that too.

Success criteria

  • npm run lint:a11y reports only findings from src/, test/ and root config files, never from .claude/worktrees/, even with nested worktrees on disk
  • npm test collects only the repo's own 17 test files, never a nested worktree's copies, and never test/dom-compat.spec.tsx
  • npm run test:dom-compat still collects exactly test/dom-compat.spec.tsx
  • npx prettier --check . ignores nested worktrees
  • git status stays clean when a nested worktree exists, for any clone (not just ones with a hand-edited .git/info/exclude)
  • Gate coverage on a CI-shaped tree (no nested worktrees) is unchanged — all of src/ and test/ still checked

How to test

  1. From a clean checkout of this branch, create two nested worktrees to reproduce the condition: git worktree add --detach .claude/worktrees/verify-a HEAD and git worktree add --detach .claude/worktrees/verify-b HEAD~1. That adds 89 lintable files and 16 spec files that don't belong to the tree under test.
  2. npm run lint:a11y → exits 0. npm run test:a11y → exits 0, 44/44. npx prettier --check . → exits 0.
  3. npx vitest list --filesOnly → exactly the 17 real files under test/, nothing under .claude/, no dom-compat.
  4. npx vitest list --filesOnly --config vitest.dom-compat.config.ts → exactly test/dom-compat.spec.tsx.
  5. To see the old behaviour for contrast, git stash the config changes and repeat step 3 — collection jumps to 31 files and vitest list errors out on the nested copies.
  6. Clean up: git worktree remove --force .claude/worktrees/verify-a && git worktree remove --force .claude/worktrees/verify-b && git worktree prune.

Coverage-parity check (this is the one that matters for CI): with no nested worktrees present, dump the file list ESLint actually reports under the old config and the new one and diff them. I ran this and the lists are byte-identical — 97 files (66 src/, 24 test/, 7 root). Vitest collects the same 17 files either way.

Security

  • No security implications

Config-only change to lint/test/format file-selection globs. No runtime, library or rendered-output code is touched, and the checks' rule sets are unchanged — only which paths they walk.

Accessibility (WCAG 2.2 AA)

  • New message types have a case entry in test/fixtures/message-cases.ts — n/a, no new message types
  • Interaction spec (<Component>A11y.spec.tsx) added/updated — n/a, no component or interaction changes

No change to rendered DOM, so the DOM-compat contract is untouched and no release-notes "Accessibility changes" entry is needed. The a11y gates keep their exact coverage (verified above): the point of the change is that lint:a11y failures now mean something again instead of being buried under 42 findings from other branches.

Additional considerations

  • This PR might have performance implications

Local runs get faster as a side effect — npm test no longer transforms and collects a duplicate copy of the suite per stray worktree. CI timing is unaffected.

Documentation Considerations

Nothing for the docs team — internal tooling only. The rationale lives in comments next to each ignore entry, and the .gitignore block cross-references the four gate configs so the set stays discoverable if one is ever edited in isolation.

Reviewer note: a Node-version-dependent test failure (not caused by this PR)

npm test exits 1 locally on Node 26, on one failure unrelated to this change: test/sanitize.spec.tssanitizeHTMLWithConfig > iframe srcdoc XSS Prevention (Zendesk Infosec Report) > preserves valid content while stripping dangerous parts in mixed srcdoc.

It fails identically with the pristine vite.config.ts, and it fails on main / release/0.78.0 in every worktree, so it is not attributable to this PR. CI is green on this branch (build (22.x) passes), which confirms it is environment-dependent, not repo debt: package.json declares engines: >=22.1.0 but CI only tests 22.x, so a developer on Node 24/26 hits a red npm test on a clean checkout of main. Worth its own ticket — either pin the supported range or fix the srcdoc expectation for newer jsdom/DOMPurify. Out of scope here.

🤖 Generated with Claude Code

…ty gates

Nested git worktrees under .claude/worktrees/<name> are full checkouts of this
repo, each with its own src/, test/ and node_modules. The local gates have no
way to know they are not part of the tree under inspection, so with stray
worktrees on disk:

- `npm run lint:a11y` reported 42 errors / 10 warnings, every one of them from a
  file inside a nested worktree and none from src/ or test/.
- `npm test` collected the nested specs on top of the real suite. Those
  worktrees' node_modules supply a second React copy, so every collected
  rendering spec died with "Cannot read properties of null (reading
  'useContext')". The root-anchored `test/dom-compat.spec.tsx` exclude also
  misses the nested copies, so dom-compat leaked into the default run.

CI never sees any of this — it does a fresh checkout with no nested worktrees —
so the failures were pure local noise that buries real findings. Git already
skips these paths via .git/info/exclude, but that file is local to one clone and
no linter reads it.

Ignore .claude/worktrees/ in both ESLint configs, both Vitest configs and
.prettierignore, and commit the .gitignore entry so the exclusion travels with
the repo instead of living in one developer's .git/info/exclude. Scoped to
worktrees/ rather than all of .claude/ so the tracked agents/commands/skills
content stays in scope exactly as it is on CI.

Verified with two real nested worktrees on disk (89 extra lintable files, 16
extra specs): lint:a11y, test:a11y and `prettier --check .` all exit 0 and
Vitest collects exactly the 17 real test files. Coverage is unchanged on a
CI-shaped tree — the linted file list is byte-identical to before (97 files: 66
src/, 24 test/, 7 root).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 10:10
@snyk-io

snyk-io Bot commented Jul 27, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adjusts local lint/test/format file-selection to ignore nested Claude Code git worktrees under .claude/worktrees/**, preventing local quality gates from picking up duplicate repo checkouts (and their node_modules) while keeping CI coverage unchanged.

Changes:

  • Exclude .claude/worktrees/** from Vitest default runs (and dom-compat config) to prevent duplicate test collection.
  • Add .claude/worktrees/** to ESLint flat-config ignores (both main + a11y gate) to avoid stray-worktree lint noise.
  • Ignore .claude/worktrees/ for Prettier and git status via .prettierignore and .gitignore.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
vitest.dom-compat.config.ts Extends Vitest default excludes to also skip nested .claude/worktrees/** during dom-compat runs.
vite.config.ts Adds **/.claude/worktrees/** to test.exclude (and refactors exclude array for commented entries).
eslint.config.js Adds .claude/worktrees/** to flat-config ignore patterns for standard linting.
eslint.a11y.config.js Adds .claude/worktrees/** to ignore list for the a11y-only ESLint gate.
.prettierignore Ignores .claude/worktrees/ to prevent formatting checks from traversing nested worktrees.
.gitignore Ignores .claude/worktrees/ so nested worktrees don’t appear as untracked files.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants