fix(hooks): stop lint.sh from modifying or discarding working tree changes - #1070
Open
k21993 wants to merge 1 commit into
Open
fix(hooks): stop lint.sh from modifying or discarding working tree changes#1070k21993 wants to merge 1 commit into
k21993 wants to merge 1 commit into
Conversation
…anges lint.sh formats in place and then runs `git checkout -- $CHANGED` to undo the formatting. That restores from HEAD, so if a touched file also has uncommitted edits, those edits are discarded along with the formatting changes. The hook is run by contributor workflows such as alignment and pre-submit checks, so this can happen without the author explicitly choosing to format files. Unformatted files already on main make it easy to hit in practice. The same step also left the tree dirty: the restore filtered to *.py, but this ruff version formats Python blocks inside Markdown, so env README files were rewritten and never restored. Ask the tools for status instead of writing: `usort check` and `ruff format --check`. Nothing is modified, so nothing needs restoring, and the failure mode changes from mutating or discarding work to reporting the problem. The gate itself is unchanged, including its scope, so an unformatted tree still fails; whether envs/ belongs in that scope is a separate question left alone here. Tests run the real hook against a throwaway git repo with a stub uv on PATH, so they are hermetic and invoke no formatter. Both failing cases are pinned: an uncommitted edit must survive a run, and a clean tree must stay clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
lint.shcurrently formats in place and then runsgit checkout -- $CHANGEDto undo the formatting. That restores from HEAD, so if a touched file also has uncommitted edits, those edits are discarded along with the formatting changes.The hook is run by contributor workflows such as alignment and pre-submit checks, so this can happen without the author explicitly choosing to format files.
The same step also leaves the tree dirty. The restore filtered to
*.py, but this ruff version formats Python code blocks inside Markdown, so envREADME.mdfiles were rewritten and never restored. In practice that shows up as roughly 31 modified READMEs after every run.The fix is to ask the tools for status instead of writing:
usort checkandruff format --check. Nothing is modified, so nothing needs restoring, and both problems go away at the root.Scope
The gate itself is unchanged, including which directories it covers, so an unformatted tree still fails the hook. Whether
envs/belongs in that scope is a separate policy question (CI formats onlysrc/andtests/) and is deliberately not touched here. This PR only changes the failure mode, from mutating or discarding work to reporting the problem.Type of Change
Alignment Checklist
.claude/docs/PRINCIPLES.mdand this PR aligns with our principles.claude/docs/INVARIANTS.mdand no invariants are violatedbash .claude/hooks/lint.shand tests and addressed all issuesTo be precise on the third box: the hook reports pre-existing formatting debt under
envs/on an untouchedmainas well, which is the policy question noted above. The files in this diff are clean underruff format --check,ruff check, andusort check, andbash -npasses on the script.RFC Status
Test Plan
Adds
tests/scripts/test_lint_hook.py, which runs the real hook against a throwaway git repo with a stubuvonPATH. No formatter is actually invoked, so the tests are hermetic and do not depend on network access or on which ruff version CI resolves, while still exercising the real shell logic rather than a reimplementation of it. They complete in about 1.5s.Four cases, two of which fail on
main:M envs/demo_env/README.mdleft modifiedThe last two are there so that "stop failing" cannot pass as a fix.
Confirmed against this repo directly. Working tree before and after a run of the real hook:
Identical, and no README churn. On
mainthe same run leaves about 31 READMEs modified.Full suite:
1534 passed, 133 skipped(baseline1530 passed, 133 skipped, plus the 4 new cases).Claude Code Review
/alignment-review: no Tier 1 issues in this diff, no principle conflicts, no RFC conflicts. The hook is contributor tooling and touches neither the agent and orchestration boundary nor client and server separation.Note
Low Risk
Contributor-only hook and test changes; no runtime product, auth, or data paths affected.
Overview
Fixes a contributor hook that could destroy uncommitted work and dirty the repo.
.claude/hooks/lint.shno longer runsusort format/ruff formatand thengit checkout --on changed*.pyfiles. It now usesusort checkandruff format --checkwith a singleFORMAT_FAILEDgate, so formatting problems are reported without writing to the tree.That removes two failure modes from automated flows (alignment review, pre-submit): reverting uncommitted edits when “undoing” formatted files, and leaving reformatted Markdown (e.g. under
envs/) dirty because only Python was restored.Adds
tests/scripts/test_lint_hook.py, which runs the real hook in a throwaway git repo with a stubuvto assert uncommitted edits survive, the tree stays clean (including.md), the hook still fails when formatting is needed, and still passes when everything is formatted.Reviewed by Cursor Bugbot for commit 7acf071. Bugbot is set up for automated code reviews on this repo. Configure here.