feat(standards): scope linting to changed files, with a hygiene opt-in - #165
Merged
twistedmelonman merged 1 commit intoSep 11, 2026
Merged
Conversation
Whole-repo linting failed PRs for debt in files their author never opened. Measured across the fleet, that was the largest single source of standards-check failures: 24 of 24 failures were markdownlint and/or shellcheck, and the markdownlint ones were dominated by three whitespace rules (MD032 blanks-around-lists, MD031 blanks-around-fences, MD049 emphasis-style) in untouched files. The local pre-commit hook already runs `markdownlint --fix` and works correctly — verified against the exact file that failed CI run 34548182880. It cannot help here, because a staged-file hook cannot fix a file the commit never staged. The gap is scope, not tooling. --changed-since REF narrows the four linters that enumerate through _tracked (shellcheck, yamllint, zizmor, markdownlint) to the files a branch changed, plus new untracked files. actionlint and the Node-floor check find their own inputs and stay whole-repo; neither has ever failed on this fleet. Whole-repo remains available and is still the default without the flag. In CI a PR narrows by default; a deliberate hygiene sweep is requested with the `standards:hygiene` label, and any non-pull_request trigger sweeps because there is no base to diff against. Two false-OK paths are closed explicitly. An unresolvable --changed-since ref exits 2 rather than computing an empty changed set, which would let every linter pass over zero files. A base-SHA fetch failure in CI falls back to a whole-repo sweep rather than to an empty diff — noisy but honest, instead of a green check that linted nothing. Tests pin both directions: a branch must not inherit debt from files it did not touch, and must still fail on violations in files it did. The dirty-file assertion also checks that the untouched file is absent from the output, so a flag that suppressed everything would fail. Hooks are disabled in the fixture via core.hooksPath, because the author's global branch protection refuses the commit and the global markdownlint --fix hook silently repairs the deliberately-bad fixture. 15/15 tests pass; shellcheck -S info clean. Advances smartwatermelon/dev-env#113. Claude-Session: https://claude.ai/code/session_01TkReZXv8XkcWNbcaiWfcvg
twistedmelonman
deleted the
claude/feat-standards-changed-since-1f470ab5
branch
September 11, 2026 01:24
1 task
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.
What was broken
standards-check failed PRs for lint debt in files the author never opened.
Measured across the fleet, that is the dominant failure mode. Of 54 runs, 24 failed — and every one was markdownlint and/or shellcheck. yamllint, actionlint, zizmor and node-floor have never failed once. The markdownlint failures are concentrated in three whitespace rules: MD032 blanks-around-lists (46), MD031 blanks-around-fences (36), MD049 emphasis-style (30) — 112 of 119 findings.
Concretely: dev-env CI run 34383919816 failed on
docs/plans/2026-08-27-workflow-sha-pinning.md, a file that commit never touched. The commit only addeddocs/local-code-review-options.md.Why the existing auto-fix does not cover it
The global pre-commit hook already runs
markdownlint --fix, and it works. Verified against the exact file that failed claude-config CI run 34548182880:--fixrepaired the MD032 violation and exited 0.It cannot help here, because a staged-file hook cannot fix a file the commit never staged. The gap is scope, not tooling.
How it was fixed
--changed-since REFnarrows the four linters that enumerate through_tracked— shellcheck, yamllint, zizmor, markdownlint — to the files a branch changed, plus new untracked files. The changed set is intersected with the existing tracked-file enumeration, so.gitignorehandling and deleted files keep exactly one answer.actionlint and the Node-floor check find their own inputs and stay whole-repo. Both are cheap and neither has ever failed on this fleet.
Whole-repo remains the behaviour without the flag. In CI:
pull_requestrun narrows to the PR's changes by defaultstandards:hygienelabel and pushing forces a whole-repo sweep, for a deliberate hygiene roundpull_requesttrigger sweeps, since there is no base to diff againstTwo false-OK paths closed explicitly
Both were live risks given this codebase's history of checks that report success while doing nothing:
--changed-sinceref exits 2 rather than computing an empty changed set, which would let every linter pass over zero filesThe run also logs its scope (
scope: files changed since <sha>/scope: all tracked files), so the coverage of a green check is recoverable from its log. That is thedev-env#113complaint.Verification
Four new assertions, 15/15 passing,
shellcheck -S infoclean.The tests pin both directions, because a flag that suppressed everything would satisfy the first assertion alone:
The fixture disables hooks via
core.hooksPath. Without that the test measures the environment instead of the flag: global branch protection refuses the commit onmain, and the globalmarkdownlint --fixhook silently repairs the deliberately-bad fixture. Both were observed doing exactly that during development.Post-merge step required
Callers pin
@standards-check-v1, which is a manually moved tag. Merging this PR changes nothing fleet-wide until that tag is retagged onto the merge commit. Without that step the PR looks done and every repo keeps the old behaviour.Advances smartwatermelon/dev-env#113.
https://claude.ai/code/session_01TkReZXv8XkcWNbcaiWfcvg