Uh oh!
There was an error while loading. Please reload this page.
fix(hooks): stop triggering full editable rebuild on every Edit/Write - #661
Conversation
Two long-standing pain points with `.claude/hooks` on this repo: 1. **Every hook fire was a `soldr cargo build --release -p fbuild-cli`.** The settings.json entries used `uv run --script ci/hooks/<name>.py` without `--no-project`. uv interpreted the cwd as the fbuild project root and ran `setuptools.build_meta.build_editable`, which fbuild's pyproject wires to the native fbuild-cli build. So every Edit/Write kicked off a release build of the CLI — minutes of latency per file change, and a hard failure whenever the underlying soldr/zccache daemon flaked (which is the very class of bug we're hitting in zccache#774 / zccache#784). Fix: add `--no-project` to every hook command. The hooks all use PEP 723 inline script metadata, so they don't actually need the project to be installed — uv was syncing it as a side-effect of project-discovery. 2. **`lint.py` ran fbuild's `./lint` against files outside this repo.** When editing files in a sibling repo's worktree (e.g. a `.claude/` worktree under `~/dev/zccache/`), `detect_crate()` read the worktree's `crates/<x>/` path segment and ran `soldr cargo clippy -p <x>` against the fbuild workspace. Either that errored (no such package) or — when names happened to collide — it lint'd the wrong code. Fix: gate `lint.py` on `file_path.startswith(PROJECT_ROOT)` so files outside this repo are no-ops. And invoke `./lint` via `sys.executable` instead of `uv run --script` so it doesn't trigger a SECOND editable rebuild on top of the outer one. Also: `.claude/tmp/` (scratch for hereoc'd issue bodies / PR descriptions during Claude sessions) added to `.gitignore`. No CI behavior change — `cargo fmt --all --check` + clippy still run via `Stop` hook on workspace-level passes. Per-file Edit hooks now finish in milliseconds instead of minutes.
Warning Review limit reached
More reviews will be available in 1 second. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. 📝 WalkthroughWalkthroughAll Claude lifecycle hook commands in ChangesClaude Hook Hardening
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ci/hooks/lint.py`:
- Around line 57-59: The boundary check using startswith() on line 58 is
performed on a non-canonicalized file_path, which allows paths containing
relative components like ".." to bypass the check and reference files outside
the repository. Canonicalize the file_path variable (by resolving it to an
absolute path with all relative components resolved) before performing the
startswith() comparison against project_root_norm to ensure only paths that
genuinely exist within the project root pass validation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 76746520-3ea9-4c6e-a8b2-59fa7ac7db6b
📒 Files selected for processing (3)
.claude/settings.json.gitignoreci/hooks/lint.py
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Default uv only watches pyproject.toml for cache invalidation, so an editable install silently leaves a stale `_native.pyd` after .rs edits. `uv run` then executes against pre-edit Rust code with no warning — a particularly silent footgun in a maturin-backed project where the .pyd is invisible to the developer. Add `[tool.uv] cache-keys` globs that cover Cargo.toml, Cargo.lock, rust-toolchain.toml, and crates/**/*.rs / crates/**/Cargo.toml. Now `uv run` triggers re-sync on real source changes. Correctness fix, not raw speed — but the fast iteration loop depends on it. Ported from FastLED/fbuild#661. Refs #2 (item 7). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Two long-standing pain points with
.claude/hooks:Every Claude
Edit/Writehook fire was asoldr cargo build --release -p fbuild-cli.settings.jsonuseduv run --script ci/hooks/<name>.pywithout--no-project. uv discovered the fbuild project and ransetuptools.build_meta.build_editable(which fbuild's pyproject wires to the native Rust build). So every file edit kicked off a release build of the CLI — minutes of latency per edit, plus a hard failure whenever the underlying soldr/zccache daemon flaked. All hook scripts use PEP 723 inline script metadata so they don't actually need the project installed.lint.pyran./lintagainst files outside this repo. When editing files in a sibling repo's worktree (e.g. zccache's),detect_crate()read the worktree'scrates/<x>/segment and ransoldr cargo clippy -p <x>against the fbuild workspace. Either errored (no such package) or — when names collided — lint'd the wrong code.Fix
--no-projectto every hook command in.claude/settings.json.lint.py: skip files outsidePROJECT_ROOT. Invoke./lintviasys.executableinstead ofuv run --scriptso the inner call doesn't trigger another editable rebuild..gitignore:.claude/tmp/(scratch dir for hereoc'd issue bodies during Claude sessions).No CI behavior change —
cargo fmt --all --check+ clippy still run via theStophook on workspace-level passes. Per-file Edit hooks now finish in milliseconds instead of minutes.Test plan
.rsfile inside this repo — hook fires fast, lints just that file.rsfile inside a sibling repo's worktree (e.g.~/dev/zccache/.claude/worktrees/.../crates/zccache/src/...) — hook skips, no spurious clippy run against fbuilduv run --no-project --script ci/hooks/lint.py < /dev/null— completes without trying to build fbuildRefs: surfaced during the zccache#784 work session — the cross-repo edit case is exactly what was misbehaving.
Summary by CodeRabbit
Chores
Refactor