Skip to content

fix: match each **/ independently, and decode git-quoted paths in diffs - #82

Merged
brentvatne merged 3 commits into
mainfrom
@brent/glob-and-quoted-paths
Oct 20, 2026
Merged

brentvatne merged 3 commits into
mainfrom
@brent/glob-and-quoted-paths

Conversation

@brentvatne

@brentvatne brentvatne commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Why

Two path-matching bugs that predate #81 and that #81's agent paths filter inherits. A multi-model review of #81 found them.

  • **/a/**/b misses some depths. patternVariants only tested a pattern with every **/ kept or every one removed. So **/ios/**/*.swift missed ios/src/A.swift and packages/ios/A.swift. This affects routing.jsonc scopes today, and will affect agent paths once feat: add a per-agent paths filter to agent frontmatter #81 lands.
  • Git-quoted paths are never decoded. Git quotes a path with special characters, and with the default core.quotePath every non-ASCII byte as octal, e.g. "b/src/caf\303\251.ts". GitHub's diffs quote the same way; I checked a real public commit diff. The parser kept the quotes and the b/ prefix, so:
    • the path matched no glob (noise filtering, routing, agent paths);
    • it pointed at a file that doesn't exist;
    • a pure rename of such a file has no ---/+++ lines, and the header regex didn't match a quoted header, so the file dropped out of the review entirely.

How

  • patternVariants tests every keep/remove combination of each **/. Patterns with more than six fall back to the old all-or-nothing pair, so the variants stop at 64.
  • The new decodeGitPath decodes C-quoted paths: octal escapes become UTF-8 bytes, plus \", \\, \t and the other C escapes. A path stays quoted, as before, when its bytes aren't valid UTF-8 or the decoded name has a control, format or line-separator character. Otherwise a PR-chosen file name containing a newline could forge an ----- END DIFF line in reviewer prompts, defeat ** matching, or make distinct invalid names collide. It runs on every path from the ---/+++ lines and from the diff --git header. The header parser accepts quoted sides, and a rename can mix a quoted side with a bare one.
  • Header lines (---, +++, rename) are read only before the first @@. Before, an added line ++ b/package-lock.json read as +++, so a PR could rename its own entry to a lockfile, which noise filtering dropped unreviewed. This bug predates feat: add a per-agent paths filter to agent frontmatter #81.
  • A pure rename takes its path from rename to, not from the header, which is ambiguous when a bare path contains b/.
  • ecr ref-check checks scope globs with the same matcher as scope assignment.
  • LLP 0004 and 0006 are updated.

Merging with #81

#81 has landed; this branch merges main and resolves the conflicts. #81's new rename from value now also goes through decodeGitPath, so a quoted rename's old path matches agent paths.

Test Plan

bun test (793 pass), bun run typecheck, bun run lint, bun run fmt:check and bun run llp:check all pass. New tests cover:

  • mixed-depth **/ matching in resolveScopes; this one fails on the old matcher;
  • quoted non-ASCII and special-character paths;
  • a quoted pure rename, including one with mixed bare and quoted sides;
  • decodeGitPath escapes;
  • a quoted rename's decoded old path;
  • paths kept quoted (newline, tab, ESC, U+2028, bidi override, invalid UTF-8);
  • forged +++/--- lines inside a hunk;
  • an ambiguous pure-rename header;
  • ref-check agreeing with runtime on a mixed-depth glob.

🤖 Generated with Claude Code

…iffs

`patternVariants` only tested a pattern with every `**/` kept or every one
removed, so `**/ios/**/*.swift` missed `ios/src/A.swift` and
`packages/ios/A.swift`. It now tests every keep/remove combination, up to six
`**/` (64 variants).

Git quotes a path with special characters, and with the default
`core.quotePath` every non-ASCII byte as octal (`"b/src/caf\303\251.ts"`).
GitHub's diffs quote the same way. The parser kept the quotes and the `b/`
prefix, so the path matched no glob (noise, routing) and pointed at a file that
doesn't exist. A pure rename of such a file has no `---`/`+++` lines, so the
header regex didn't match either, and the file dropped out of the review
entirely. `decodeGitPath` now decodes every path from the `---`/`+++` lines and
from the header.
…d-paths

# Conflicts:
#	llp/0004-diff-noise-and-prompts.explainer.md
#	src/__tests__/diff.test.ts
#	src/config/routing.ts
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

🤖 AI code review

Decision: Ready for human review

Overall PR risk: Medium. This change modifies how diffs parse paths and match patterns against scopes. It decodes git-quoted paths (which may change their effective filenames) and expands **/ pattern matching to cover more depth combinations. Files with special characters or in scope patterns with multiple **/ wildcards may now assign to different scopes than before. Rollback is direct via reverting the core diff and routing parser files.

🔗 Review setup:

  • This PR changes code the reviewer prompts cite (src, src/tests). Check that the guidance quoting it is still correct.

No findings.


This review is advisory — it never blocks a merge and never auto-approves.

Review of #82 found that decoding quoted paths into raw characters opened new
problems, and that the parser had two older ones in the same function.

- `decodeGitPath` now keeps a quoted path as is when its bytes are not valid
  UTF-8 or the decoded name has a control, format or line-separator character.
  A decoded newline in a PR-chosen file name could forge an `----- END DIFF`
  line in reviewer prompts (`sanitizeUntrusted` keeps newlines). A newline or
  U+2028 also stopped `**` from matching. Lenient UTF-8 turned distinct invalid
  names into one U+FFFD name.
- Header lines are read only before the first `@@`. An added line
  `++ b/package-lock.json` read as `+++`, so a PR could rename its own entry to
  a lockfile, which noise filtering then dropped unreviewed.
- A pure rename takes its path from `rename to`, not the header, which is
  ambiguous when a bare path contains " b/".
- `ecr ref-check` checks scope globs with `matchesPathGlobs`, the matcher scope
  assignment uses, instead of its own copy of the old all-or-nothing variants.
@brentvatne
brentvatne merged commit 1461f37 into main Oct 20, 2026
3 checks passed
@brentvatne
brentvatne deleted the @brent/glob-and-quoted-paths branch October 20, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant