Skip to content

fix(llm): keep quoted-path diff sections in the commit-message prompt - #4041

Merged
max-sixty merged 2 commits into
mainfrom
nightly/clean-34195536103
Sep 8, 2026
Merged

fix(llm): keep quoted-path diff sections in the commit-message prompt#4041
max-sixty merged 2 commits into
mainfrom
nightly/clean-34195536103

Conversation

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

A commit or squash message generated over a large diff silently omitted any file whose name git quotes. parse_diff_sections read the destination path with line.split(" b/").nth(1), and a quoted header carries no bare b/ — so the path came back None and the section was never pushed onto the list, taking the file's diff out of the prompt with it. The file still appears in the diffstat, so the model is told the file changed and shown nothing of how.

Two headers hit this, and both are ordinary rather than exotic:

$ git diff | grep '^diff --git'
diff --git a/plain.txt b/plain.txt
diff --git "a/\303\251.txt" "b/\303\251.txt"

core.quotePath is on by default, so any non-ASCII filename quotes — accented names, CJK, emoji. A name containing " or \ quotes regardless of that setting. Reaching the drop needs the diff over DIFF_BUDGET (100 KB), since a diff under budget passes through whole; over it, parse_diff_sections is the only view of the content.

This is the same trap #3486 hit in step copy-ignored, fixed by #3487.

Fix

parse_diff_header_path reads the quoted form too, keying on the "b/ that DIFF_PREFIX_OVERRIDES already pins. It returns the escaped name (\303\251.txt), which is what the caller needs: the name feeds is_lock_file's suffix match, never the filesystem.

A header whose path still doesn't parse now opens a section anyway, with an empty name. Losing the name costs lock-file filtering for that one file; losing the section costs the diff. A path that itself contains b/ stays ambiguous — git's own plain-text output cannot express that either, which is why --numstat -z exists.

Tests

Three regression tests, each failing before the fix:

  • test_parse_diff_sections_quoted_paths — both quoted shapes parse, and the concatenated sections still reproduce the input byte-for-byte.
  • test_parse_diff_sections_unparsable_header_keeps_content — an unreadable header keeps its content.
  • test_prepare_diff_keeps_quoted_path_sections — end to end through prepare_diff over budget, which is the path that reaches users.

cargo test --lib --bins and cargo clippy --all-targets --all-features are clean locally.

git quotes a `diff --git` path pair when the name is non-ASCII (`core.quotePath`, on by default) or holds `"`/`\\`. Neither header contains a bare ` b/`, so `parse_diff_sections` found no path and dropped that file's diff from the over-budget prompt entirely.

Parse the quoted form as well, and open a section on any `diff --git` header whether or not its path parses — the name only feeds lock-file filtering.
@worktrunk-bot worktrunk-bot added the nightly-cleanup Issues found by nightly code quality sweep label Sep 8, 2026

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two of the new test literals use the forms CLAUDE.md rules out — "never \ continuation (silently strips following whitespace) or concat!()" — and here the rule's own rationale is what makes it worth changing: the content is dense with " and \, so the r#"…"# form the other tests in this module already use is both the mandated one and the readable one. Suggestions inline; both compile, pass, and leave cargo fmt --check clean.

Separately, and a decision rather than a change to this PR: core.quotePath=false in DIFF_PREFIX_OVERRIDES would drop the non-ASCII case at the source and put real filenames into the prompt — in the diffstat as well as the diff — instead of \303\251.txt. It does not replace the parser fix, since a name holding " stays quoted whatever that setting says (confirmed against git: with core.quotePath=false, é.txt stops quoting while the header for we"ird.lock is still diff --git "a/we\"ird.lock" "b/we\"ird.lock"). Growing the overrides — and the two --stat calls that don't carry them — is its own call, so leaving it as a note.

Comment thread src/llm.rs Outdated
Comment thread src/llm.rs
CLAUDE.md rules out `concat!()` and `\` continuation for multiline
strings. Both new fixtures are dense with " and \, so the r#"…"# form
the rest of the module already uses is both the mandated one and the
readable one — the escaped git output now reads verbatim.
@worktrunk-bot

Copy link
Copy Markdown
Collaborator Author

On the core.quotePath=false note: leaving it out of this PR, and I don't think it should ride along as a follow-up without a call from @max-sixty.

It is not a substitute for the parser fix — a name holding " quotes whatever that setting says, which is what test_parse_diff_sections_quoted_paths' we"ird.lock case pins. As an improvement it is real but partial: DIFF_PREFIX_OVERRIDES is applied only to the git diff call, while the two --stat calls next to it run bare, so setting it there would put real filenames in the diff and leave escaped ones in the diffstat the same prompt carries — a worse mismatch than today's consistent escaping. Doing it properly means the override moving to all three call sites, which is the growth in the override list the review flagged as its own decision.

@max-sixty
max-sixty merged commit 3a98379 into main Sep 8, 2026
43 checks passed
@max-sixty
max-sixty deleted the nightly/clean-34195536103 branch September 8, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

nightly-cleanup Issues found by nightly code quality sweep

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants