fix(skills): stop walking the whole tree to answer "does any file match" - #1213
fix(skills): stop walking the whole tree to answer "does any file match"#1213sahrizvi wants to merge 1 commit into
Conversation
`applyPaths` auto-load asks one question per skill — does at least one file
in the worktree match this glob — and answered it with
`Glob.scan(...).length > 0`. `scan` resolves only once the entire walk has
finished, so every skill paid for the full tree even when the first
directory already answered.
Two builtin skills ship with `applyPaths` (`dbt-develop`,
`dbt-schema-verify`), so every session pays this twice, before the first
token, with no configuration and no workspace involved.
The cost depends entirely on what the worktree resolves to. Inside a git
repo it is the repo root and the scans take ~10ms. Outside one,
`Project.fromDirectory` returns the global project whose worktree is `/`,
and the two scans walk the entire filesystem.
Measured from a directory outside a git repo, same binary, same prompt:
scan(...).length > 0 48.4 52.9 51.6 s
Glob.exists (this change) 9.1 6.8 6.4 s
scan removed entirely 7.1 6.8 6.7 s (floor)
Inside a repo: 7.5 / 7.3s, unchanged.
`Glob.exists` uses `globIterate`, which yields lazily, so the walk is
abandoned at the first match. It takes the same options as `scan` — the
tests pin `include`, `ignore` and the missing-directory case, and fail if
the options stop being forwarded.
Also tried and rejected: passing `ignore: Glob.DEFAULT_IGNORE` here, the
way #1184 did for the MCP scans. It made this *slower* — 61-82s against a
~51s baseline — because outside a repo the tree is not dependency-heavy,
so every candidate path pays 12 minimatch tests and almost nothing gets
pruned. Early exit is the right lever for an existence check.
Not addressed here: the worktree being `/` outside a git repo. That makes
skills auto-load off unrelated files elsewhere on the machine, which is a
correctness question for whoever owns project identity.
core suite: 1072 pass, 26 fail — the same 26 fail on unmodified main.
session/skill suites: 1516 pass, 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe glob utility adds a lazy ChangesGlob existence checks
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to This PR changes file-existence checks to stop after the first match, reducing startup time without changing the matching scope or behavior. No actionable merge-blocking risk remains after normal checks and review. Sequence Diagram(s)sequenceDiagram
participant SystemPrompt
participant GlobExists
participant globIterate
SystemPrompt->>GlobExists: Check applyPaths pattern
GlobExists->>globIterate: Traverse with matching options
globIterate-->>GlobExists: Return on first match
GlobExists-->>SystemPrompt: Return boolean
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Description checkExplanation The description explains the issue, implementation, performance impact, verification results, tests, limitations, and checklist status. It omits the template's Issue section and issue number, but the required change information is otherwise complete.
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (3 files)
Reviewed by deepseek-v4-pro · Input: 34K · Output: 15.2K · Cached: 334.1K Review guidance: REVIEW.md from base branch |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Type of change
What does this PR do?
Skill
applyPathsauto-load asks one question per skill — does at least one file in the worktree match this glob — and answered it withGlob.scan(...).length > 0.scanresolves only after the entire walk completes, so each skill paid for the whole tree even when the first directory already answered.Two builtin skills ship with
applyPaths(dbt-develop,dbt-schema-verify), so every session pays this twice, before the first token — no configuration, no custom skills, no workspace required.How much it costs depends on what the worktree resolves to:
Instance.worktree/—Project.fromDirectoryreturns the global project (project.ts:293)Glob.existsusesglobIterate, which yields lazily, so the walk is abandoned at the first match.How did you verify your code works?
Same binary, same prompt, same account — only the implementation differs. Run from a directory outside a git repo:
scan(...).length > 0(current)Glob.exists(this PR)Inside a repo: 7.5, 7.3s — unchanged. The fix lands within ~1s of the floor, so the remaining startup is not this scan.
Traces confirm where the time went: a session with 73.3s of startup returned its first generation in 0.08s. The model was never the bottleneck.
Tests: four cases on
Glob.existspinning agreement withscan,include: "file"behaviour,ignorepruning, and a missing directory. Mutation-tested — dropping the options forwarding fails two of them.packages/core: 1072 pass, 26 fail — the same 26 fail on unmodifiedmain(verified by stashing this change and re-running).test/session+test/skill: 1516 pass, 0 fail.bun typecheck: 13 tasks clean. Marker Guard: passes. No new Prettier violations.Rejected alternative
Passing
ignore: Glob.DEFAULT_IGNOREhere, mirroring what #1184 did for the MCP scans, makes it slower: 61–82s against a ~51s baseline. Outside a repo the tree is not dependency-dominated, so every candidate path pays 12 minimatch tests while almost nothing gets pruned. Early exit is the right lever for an existence check; pruning is the right lever when the tree really is mostlynode_modules.Likely root cause
Two independent things combine; neither is a problem alone.
1.
worktreeis/outside a git repo — present since the initial import.Project.fromDirectorywalks up looking for.git; finding none, it returns the global project withworktreeandsandboxhardcoded to/(project.ts:293). That line is original to the repository's first commit (f2cd5c124, 2026-03-01) and carries noaltimate_changemarkers, so it is inherited upstream code rather than something added here. As a sentinel meaning "no project", it was harmless — nothing walked it.2. A per-skill worktree scan — added 2026-05-29.
anyMatchInWorktree(#849,a490bd45e) walksInstance.worktreeonce perapplyPathsskill, and the same change shipped two builtin skills carryingapplyPaths. Inside a repo that root is bounded and each scan costs ~10ms, which is what the code was written against — the comment on the function reasons explicitly about catchingdbt_project.yml"no matter how deep the user's cwd is". Outside a repo, the same call inherits/.So the combination has existed since 2026-05-29, and what determines whether anyone feels it is simply where the CLI is launched from: inside a git repo it is invisible, outside one it costs ~45s per session.
No evidence of a recent regression. The function is byte-identical since it landed, the two builtin skills gained
applyPathsin that same commit, and worktree resolution has not changed since the import. Reports clustering recently are more consistent with where sessions are being started than with any code change — a same-machine, same-account A/B differed by 51s vs 8s on working directory alone.This PR fixes the second half, which is the part that turns an inert sentinel into a filesystem walk. The first half is noted below.
Not addressed here
Project.fromDirectoryhardcodesworktree: "/"when no.gitis found. Beyond performance, that means skills auto-load based on unrelated files elsewhere on the machine — with this PR applied, the scan returnsmatched=truein 11ms atroot=/because some unrelateddbt_project.ymlexists on disk. That is a correctness question for whoever owns project identity, and deliberately left out of this change.Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
Summary by cubic
Replaces
Glob.scan(...).length > 0in skill auto-load with a newGlob.existsthat stops walking at the first match, cutting startup outside a git repo from ~51s to ~7s; startup inside a repo is unchanged.Glob.existstopackages/coreusingglobIterate, which yields lazily and abandons the walk at the first hit.applyPathscheck inpackages/opencodeto the new call; two builtin skills (dbt-develop,dbt-schema-verify) run it every session.ignore: Glob.DEFAULT_IGNOREwas tried and rejected — it made startup slower (61–82s) because paths outside a git repo rarely get pruned.worktreeis/, so skills can still match unrelated files elsewhere on the machine; that's left for whoever owns project identity.scan,include/ignorehandling, and a missing directory.Written for commit c7e9371. Summary will update on new commits.
Summary by CodeRabbit
New Features
Performance
Bug Fixes