Catch bare head/tail in the truncation guard - #24
Open
chaosisnotrandomitisrhythmic wants to merge 1 commit into
Open
Conversation
_TRUNC requires an explicit line count, so a bare `head` or `tail` at the end of a pipe never matches. Both default to 10 lines, which is the same cut as the `-10` form the guard already rejects. The guard names the two commands it is about and then misses them whenever the count is left off. The regex now has two alternatives: an explicit count under 20, or no count at all. Counts of 20 and above, byte counts, follow mode, and commands that merely start with those four letters are all unaffected.
chaosisnotrandomitisrhythmic
added a commit
to chaosisnotrandomitisrhythmic/aai-coding
that referenced
this pull request
Sep 5, 2026
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.
_TRUNCrequires an explicit line count, so a bareheadortailat the end of a pipe never matches. Both default to 10 lines, which is the same cut as the-10form the guard already rejects.The
\s+and the digit class are both mandatory. The guard names the two commands it is about, and then misses them whenever the count is left off.Why it matters
The hook exists to stop truncation that is decided before the output exists. Leaving the count off does not weaken that decision, it just spells it differently:
tar tzf x.tgz | head -10tar tzf x.tgz | headls ~ | tailMeasured over four days of one developer's sessions, the guard fired 55 times on truncating pipes. In the same corpus a rejected
| head -1was retried moments later as| sed -n 1pand passed, which is the same shape of hole: the rule matches a notation rather than the behaviour. This PR closes thehead/tailhalf, where the bypass is the guard's own two commands.sed -n '1,10p'andawk 'NR<=10'truncate identically and are still allowed; they feel like a separate question, since neither is what the message tells you to fix.The fix
Two alternatives instead of one: an explicit count under 20, or no count at all.
The bare branch is a lookahead, so it only fires where the command actually ends: at end of string, or before
|,;,&or a newline. That keepshead -30,head -c 200,tail -fandls | headerout of it.Tests
Added to
test_bash_guard, all failing before the change:Every pre-existing assertion in
test_bash_guardstill passes unchanged, including the-20and-50boundaries and thehead -3 file.txtcase with no pipe.Note on running the suite
uv run pytestcould not resolve dependencies on this checkout, before and after the change:pyproject.tomldeclaresrequires-python = ">=3.10"whilellmdojoneeds>=3.11. I verifiedbash_guard_msgdirectly instead, which needs only the standard library: 14 cases that must be blocked and 11 that must pass, 0 failures. Happy to split therequires-pythonbump into its own PR if it is wanted.🤖 Generated with Claude Code