Skip to content

fix: filename quoting, nested globs, and small display bugs - #3

Merged
getdownandcode merged 2 commits into
mainfrom
feat
Sep 9, 2026
Merged

getdownandcode merged 2 commits into
mainfrom
feat

Conversation

@getdownandcode

Copy link
Copy Markdown
Owner

Follow-up hardening to the Phase 1 DiffExtractor, from the review findings plus one extra bug caught by end-to-end probing.

What was wrong

  1. Quoted paths never aligned. Git C-quotes ", backslash, and control
    characters even with quotepath=false, while -z --numstat is always raw.
    A file like back\slash.py produced two FileDiff rows: the real path
    with counts but no patch, plus a quoted phantom row. Same class of bug
    for " in names.
  2. Quoted diff --git headers dropped whole patches. A quoted header
    ("a/X" "b/Y") matched neither the symmetric check nor the space split,
    so the chunk was skipped and patch_text came back empty.
  3. Nested monorepo output not filtered. dist/* only matched root-level
    dist/; packages/ui/dist/bundle.js sailed through the noise filter.
  4. budget_cap_usd: 0.0 displayed as none in config show
    (falsy check).
  5. Non-English locales / non-UTF-8 output could escape the typed-error
    path (unmatched message check; raw traceback on decode failure).

Fixes

  • _unescape_c_quoting decodes \\, \", \n, \t, and octal runs (as
    UTF-8 bytes, matching git's per-byte escaping); applied everywhere quoted
    paths are read (patch headers, +++/---, rename from/to, name-status).
  • Quoted diff --git headers split between the quoted strings.
  • Dir-prefix ignore globs match the directory name at any depth.
  • LC_ALL=C on all git invocations for deterministic English errors;
    non-UTF-8 git output raises clean GitCommandError instead of a traceback.
  • config show uses is not None for the budget.
  • Tool-dropped .agents/ directory added to .gitignore (it was breaking
    ruff check . on unrelated vendored code).

Verification

  • 51/51 pytest green (new: backslash/quote filenames, non-UTF-8 fake-git
    output, nested monorepo dist, --- re-key break, zero-budget display).
  • ruff check, ruff format --check, mypy --strict clean.
  • Hostile end-to-end probe (spaced + non-ASCII + backslash names, nested
    dist, mnemonicPrefix=true): each file exactly once with correct status,
    counts, and full patch text.

Deliberately not changed: the three-call git design (race/perf concern
declined — millisecond window, ~3 cheap subprocesses) and the parser
structure (targeted fixes instead of a rewrite).

- Decode git C-style quoting (backslash, quotes, octal) so quoted patch and status keys align with raw -z numstat keys
- Parse quoted diff --git headers by splitting between quoted strings
- Match dir-prefix ignore globs at any depth for monorepos
- LC_ALL=C for deterministic English git errors; clean GitCommandError on non-UTF-8 output
- config show prints 0.0 budget instead of none; ignore tool-dropped .agents/ dir
@getdownandcode

Copy link
Copy Markdown
Owner Author

Verified both against the running code — R1 exactly as reported, R2 real but narrower. Thanks for the byte-level probes; details:

R1 confirmed (content loss). git mv plain.txt 'weird"name.txt' + an edit gives header diff --git a/plain.txt "b/weird\"name.txt". Neither the symmetric check nor rfind(" b/") matches, so the chunk is skipped before the rename to override or +++ re-key can save it. Reproduced: correct path/status/counts, patch_text == "" despite real hunks.

R2 confirmed, narrower trigger. Rename with edits into a/ is actually saved by the +++ re-key (my repro: patch len 178, intact). It bites only on pure renames: rename to a/foo.py gets prefix-stripped to foo.py (verified: parser keys ['foo.py'], lookup misses). Still categorically wrong — git never prefixes those lines — so fixing regardless.

One correction to the suggested snippet: rfind(' "b/') returns the index of the opening ", so rest[:idx] drops the closing quote and rest[idx+1:] keeps a leading space. I'm landing this instead:

if rest.endswith('"'):
    idx = rest.rfind('" "b/')
    if idx == -1:
        return None, None
    return _strip_git_prefix(rest[: idx + 1]), _strip_git_prefix(rest[idx + 2 :])

(plus _decode_quoted instead of _strip_git_prefix on rename/copy from/to lines). Fix + regression tests incoming on this branch.

- Split diff --git headers across all four quoted/unquoted combinations
- Stop stripping a//b/ from rename/copy from/to lines (never prefixed by git)
- Regression tests: plain<->quoted renames with edits, pure rename into a/ dir
@getdownandcode

Copy link
Copy Markdown
Owner Author

Both fixed in bf649ff, now on this branch: mixed quoted/unquoted headers split on the Y-side quote boundary (all 4 combinations probed correct), and rename/copy from/to lines decode quoting without prefix-stripping. Added 3 regression tests (plain<->quoted renames with edits, pure rename into a/ dir). Full gate green: 54 pytest, ruff check, format --check, mypy --strict.

@getdownandcode

Copy link
Copy Markdown
Owner Author

All reported issues and regressions (including mixed quoted/unquoted headers and directory prefix preservation) have been verified fixed. All 54 tests are passing cleanly. LGTM!

@getdownandcode
getdownandcode merged commit b68cc85 into main Sep 9, 2026
2 checks passed
getdownandcode added a commit that referenced this pull request Sep 19, 2026
…d orchestrator consolidation

- #1 (Blocker): Fail loud with exit code 1 on git errors, budget cap breaches, and gh CLI errors across pr-summary and changelog CLI commands.
- #2: Prevent duplicate printing when no changes/commits exist by early returning None.
- #3: Route diagnostic warnings, budget alerts, and error messages to stderr console (err_console) to keep stdout clean for Unix piping.
- #4: Correctly parse and surface breaking changes (exclamation mark and BREAKING CHANGE: footers) in conventional commits and templates.
- #5: Bump changelog template to version 2, including from_ref..to_ref range and explicit release headers.
- #6: Support --title (-t) flag in pr-summary and propagate gh pr create failures.
- #7: Remove dead head argument from fallback PR summary.
- #8: Consolidate duplicate budget checking, safe telemetry recording, and heuristic token counters into orchestrator.py.
- #9: Enforce strict type hints with CommitLogEntry in fallback.py.
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