feat: exclude generated files from the review diff by default - #88
feat: exclude generated files from the review diff by default#88aliasunder wants to merge 5 commits into
Conversation
New diff_exclude_paths input: folder prefixes or globs removed from the review diff before the token budget check, so one oversized generated artifact no longer skips the whole review. Supplied patterns extend a built-in default list (ecosystem lockfiles, minified sources, source maps); a leading "none" drops the defaults. Patterns over 2 stars per segment are rejected — glob matching backtracks exponentially. New respect_linguist_generated input (default on): files the repo's root .gitattributes marks linguist-generated=true are excluded too; negated entries keep a default-list match reviewable. Excluded files stay visible — named with their source in the annotated diff trailer, the status comment's context notes, and the run log. When every changed file matches, the run posts a skip review naming the mechanism. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
umm-actually re-reviewed at 3 new finding(s) posted (6 tracked finding(s) across all runs). Context notes
umm-actually · deepseek/deepseek-v4-flash-0731 |
- Diff-excluded changed files are excluded from the related-file and related-doc scans so their content cannot re-enter the prompt after the partition removed them from the review subject - The all-excluded skip attributes each exclusion to the layer that actually excluded it and lists every excluded file in the skip body - Port the Prettier CHANGELOG.md fix from main (the release flow committed an unformatted entry, failing the checks job on every PR) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
matchesGlob's globstar matches zero directories, so the shipped **/-prefixed defaults catch the standard npm layout's root lockfile — pinned so a future matcher swap cannot regress it silently. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The release flow's update-changelog.sh writes it directly to main without a format pass, so checking it fails every PR's checks job after a release until someone hand-formats a generated file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
npm's own output format is the source of truth for the lockfile; formatting it invites churn against what the tool regenerates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Name the real exclusion sources and files in the all-excluded skip
The all-excluded skip path hard-codes 'diff_exclude_paths' as the exclusion source, so a PR touching only package-lock.json (matched by the built-in default list or a repo .gitattributes linguist rule) is reported as caused by an input the operator never configured. On this path no trailer and no status comment are built, so the excluded file and its true source appear nowhere user-visible, contradicting the README/action.yml claim that every exclusion is named. Failure scenario: A repo with default inputs receives a PR changing only root-level package-lock.json: the run posts 'Skipped — all 1 changed files match diff_exclude_paths' in the skip review and check title, although no diff_exclude_paths pattern was configured (the file matched the built-in default list); the file itself is named nowhere in the review output. Suggested fixBuild the skip reason (and a per-file list) from the actual excludedDiffFiles results, naming each file and its exclusion source (e.g. 'all 1 changed file(s) excluded from review: src/package-lock.json (default exclusion)') instead of hard-coding 'match diff_exclude_paths'; if the reason string must stay generic, append the per-file source list so the skip body matches the documented visibility promise.umm-actually · deepseek/deepseek-v4-flash-0731 |
|
Reject malformed globs in diff_exclude_paths at config parse
Config validation for diff_exclude_paths checks only the per-segment wildcard count, so a malformed glob such as '[' passes parse and throws ERR_INVALID_GLOB_PATTERN at runtime inside matchesGlob, failing every review until the config is fixed instead of rejecting the input at startup like other config errors. Failure scenario: An operator sets diff_exclude_paths: '[' (comma-split yields one pattern '['). Config parsing succeeds (0 stars), and the next PR review crashes mid-pipeline in matchesExcludePattern with an invalid-glob exception, failing the check run and the action instead of rejecting the input at boot. Suggested fixAfter the wildcard-cap check in diffExcludePathsInput, probe each pattern with posix.matchesGlob('probe', pattern) inside a try/catch and ctx.addIssue(...) on throw, so malformed globs fail at config parse like the existing wildcard-cap violations.umm-actually · deepseek/deepseek-v4-flash-0731 |
|
The two beyond-diff comments from the
Generated by Claude Code |
What
A PR dominated by regenerated artifacts (test snapshots, lockfiles, minified bundles) could blow the diff token budget and skip the entire review — the hand-written changes got nothing. And even under budget, generated hunks waste model attention and cost on every run.
This adds diff-level exclusion with three tiers:
diff_exclude_pathsinput — folder prefixes or globs removed from the parsed diff before annotation and the budget check. Supplied patterns extend a built-in default list of generated artifacts (ecosystem lockfiles,*.min.js,*.min.css,*.map— the classes GitHub's linguist auto-collapses via built-in rules no.gitattributescan express). A leadingnonedrops the defaults:nonealone disables exclusion,none, evals/**replaces the list. Empty means the default list (documented divergence fromexclude_paths, where empty means no exclusions), so bare repo-variable wiring works.respect_linguist_generatedinput (default on) — changed files the repo's root.gitattributesmarkslinguist-generated=trueare excluded too; negated entries (-linguist-generated) keep a file reviewable even when the default list matches it. Explicitly supplieddiff_exclude_pathspatterns always win. Rules are read from the PR head deliberately: a PR that introduces the.gitattributesmarks reviews under its own rules..snap/__snapshots__diffs expanded, so collapsing them is a repo-level decision (.gitattributes) or workflow-level one (the input), never ours.Behavior
=== path ===diff headers; the anchoring contract already routes findings about un-rendered files onto a provided file, and a finding naming an excluded file is dropped by the unknown-file filter (tested).neutralcheck conclusion) whose reason counts exclusions per source —all N changed file(s) excluded from review (2 by default exclusion, 1 by linguist-generated)— and whose body lists every excluded file with its source, so the cause is never misattributed to an input the operator didn't set.*in one segment are rejected at config parse; a violating.gitattributesrule — untrusted, PR-author-controlled input — is dropped with a warn and never fails the run.Implementation notes
src/diff/:exclude-diff-files.ts(partition + trailer renderer),gitattributes.ts(linguist-rule parser + evaluator),pattern-safety.ts(the shared wildcard cap).context/workspace.tsgainsreadGitAttributes()(root file only — documented v1 boundary; symlink-escape and read failures degrade to no rules with a warn).exclude_pathsrule) or globs vianode:pathmatchesGlob— union, no new dependency. Gitattributes patterns use gitignore semantics (slash-less patterns match basenames at any depth) via theignorepackage (new runtime dependency, pinned to the probed 5.x major), instantiated per pattern — load-bearing: a shared instance would activate gitignore!-negation semantics the gitattributes format forbids. Directory-style entries (dir/, baredir) over-approximate toward exclusion relative to the gitattributes spec — ruled deliberate, pinned in tests.Testing
nonepositions, extend-not-replace, wildcard-cap rejection), gitattributes parser/evaluator (all four attribute spellings, last-match-wins, cap-violation warn-drop, forbidden!patterns, escaped spaces), partition precedence (negation exempts defaults, operator beats negation, rename directions, deletions, root-level files against the shipped**/defaults), trailer full-string format, exclusion-source summaries, and orchestrate wiring (all-excluded skip with per-source attribution and file list, budget pass with oversized files excluded, context note, unknown-file drop of a finding naming an excluded file, diff-excluded paths forwarded to the related-file/doc scans,respect_linguist_generated: falseshort-circuit).🤖 Generated with Claude Code