Skip to content

fix(tui): preserve Windows verbatim-path operands through POSIX word split - #5610

Merged
Hmbown merged 3 commits into
Hmbown:mainfrom
aboimpinto:fix/windows-verbatim-operand-classification
Aug 25, 2026
Merged

fix(tui): preserve Windows verbatim-path operands through POSIX word split#5610
Hmbown merged 3 commits into
Hmbown:mainfrom
aboimpinto:fix/windows-verbatim-operand-classification

Conversation

@aboimpinto

@aboimpinto aboimpinto commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the two Windows CI failures that block FEAT-019 (PR #5609):

  • tools::shell::tests::readonly_operands_are_workspace_bounded_and_symlink_aware
  • tools::subagent::tests::read_only_inspection_roles_execute_pwd_and_absolute_git_log

Root cause

enforce_readonly_workspace_operands (and hardened_readonly_argv) tokenize the read-only command with shell_words::split, which follows POSIX backslash-escaping. On Windows, Path::canonicalize returns verbatim device paths (\\?\C:\...); the POSIX splitter silently eats the separators of any backslash path (C:\Users\...C:Users..., \\?\C:\...\?C:...).

The previous fix (4285710) stripped the \\?\ prefix after the split and only for key=value operands (token.split_once('=')). The failing tests pass the canonical path as a bare space-separated token (cat <path>, git -C <path> log), so the splitter already mangled it before the strip could run — the refusal then happened on the mangled spelling.

Fix

Normalize Windows absolute path spellings before any POSIX-style splitter or glob-charset gate via a shared normalize_windows_command_paths in crates/tui/src/command_safety.rs:

  • strips the \\?\ verbatim prefix (Path::canonicalize artifact) whose ? trips the classifiers' glob-charset gate; \\.\ device paths are preserved verbatim;
  • doubles the backslashes of Windows-absolute-path-like words (drive letter + :, verbatim, or UNC prefix) so shlex/shell_words round-trip the real path instead of C:\Users\... collapsing to C:Users....

Applied at all judging sites:

  • is_agent_readonly_shell_command and is_parallel_readonly_command (command_safety.rs) — the subagent posture gate;
  • both shell_words::split sites in shell.rs (enforce_readonly_workspace_operands and hardened_readonly_argv).

Only Windows-absolute-path-like words are touched — POSIX escapes (echo a\ b) and unix hosts are unaffected.

Testing

  • cargo fmt --all -- --check
  • New unit tests: verbatim/drive/UNC/device operands survive the POSIX split round-trip; the agent classifier admits verbatim-prefixed git -C commands; POSIX escapes and drive-relative spellings untouched
  • tools::shell module — 139 passed
  • tools::subagent module — 555 passed
  • Clippy -D warnings on codewhale-tui — 0 warnings

The definitive validation is the Windows CI job (Linux cannot reproduce verbatim-path canonicalization); a green Test (windows-latest) on this PR unblocks FEAT-019.

Paulo Aboim Pinto

No-Issue: this fix unblocks FEAT-019 (PR #5609); umbrella #5316 must remain open for the remaining decomposition FEATs.

Paulo Aboim Pinto

@aboimpinto
aboimpinto requested a review from Hmbown as a code owner August 25, 2026 08:32
Paulo Aboim Pinto added 2 commits August 25, 2026 10:42
…split

shell_words splits with POSIX backslash-escaping, which silently eats the
separators of Windows absolute paths (C:\Users\... becomes C:Users...) and
mangles the \\?\ verbatim prefix before the readonly operand classifier
can see it. The 4285710 strip only handled key=value operands after the
split; bare-token operands (cat <path>, git -C <path>) never reach it.

Protect Windows-absolute-path-like words by doubling their backslashes
before both shell_words::split sites (enforce_readonly_workspace_operands
and hardened_readonly_argv); the splitter round-trips the real path and
the existing verbatim strip then classifies it correctly. POSIX escapes
and unix hosts are untouched.

Unblocks the Windows CI failures on FEAT-019 (PR Hmbown#5609):
- tools::shell::tests::readonly_operands_are_workspace_bounded_and_symlink_aware
- tools::subagent::tests::read_only_inspection_roles_execute_pwd_and_absolute_git_log
…ility

The v0.9.10..HEAD release-note receipt gate requires the exact PR number
referenced by a feature commit. Upstream 6110159 cites Hmbown#5604 in its
subject but its changelog entry only carries Hmbown#5589; add the Hmbown#5604
reference so the Version drift gate passes on any PR based on current
main.
@aboimpinto
aboimpinto force-pushed the fix/windows-verbatim-operand-classification branch from 3ba4003 to 064c7e1 Compare August 25, 2026 08:43
…rs too

The subagent posture gate judges commands through
is_agent_readonly_shell_command / is_parallel_readonly_command in
command_safety.rs, which split with shlex (POSIX backslash escaping)
and reject '?' as a glob char. The \\?\ verbatim prefix produced by
Path::canonicalize trips the ? gate and shlex eats the backslashes,
so 'git -C \\?\C:\... log' was refused at the posture gate even
with the shell.rs split fix.

Move the normalization to a shared command_safety helper: strip the
\\?\ prefix (the only spelling with the ? glob problem; \\.\
device paths are preserved verbatim) and double the backslashes of
Windows-absolute-path-like words so shlex/shell_words round-trip the
real path. Apply it at both classifiers and both shell.rs split
sites; POSIX escapes and unix hosts are untouched.
@Hmbown

Hmbown commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Thank you for this — I verified it and it is doing more work than the title suggests: this PR unblocks the whole open-PR queue.

main (6110159) is currently red on two required checks, and every open PR inherits both:

  1. Test (windows-latest)tools::shell::tests::readonly_operands_are_workspace_bounded_and_symlink_aware and tools::subagent::tests::read_only_inspection_roles_execute_pwd_and_absolute_git_log fail identically on feat(tui): show tool and MCP schema costs (#5603) #5611 and refactor(tui): adopt command shapes in memory group (FEAT-019) #5609. Your diagnosis is right and it is the part feat(runtime): 0.9.12 relay integration — unify managed Chat with native runtime threads #5606 missed: feat(runtime): 0.9.12 relay integration — unify managed Chat with native runtime threads #5606 stripped the \\?\ prefix at classification time, but shell_words::split had already eaten the separators, so the operand arrived as \\?C:UsersrunneradminAppData.... Normalizing before the split is the correct place.
  2. Version drift — reproduced against origin/main:
    ::error::Feature commit 6110159bd0bb references #5604, but no release-note receipt exists in CHANGELOG.md docs/CHANGELOG_ARCHIVE.md.
    
    Your (#5589)(#5604, covers #5589) edit is exactly the right fix, and it is the only PR in the queue carrying it.

Your run is green on both Version drift and Test (windows-latest) (only Test (macos-latest) was still running when I checked).

One non-blocking observation for the record, not a change request: normalize_windows_command_paths runs the \\?\ strip and the backslash-doubling on all platforms, and its output feeds hardened_readonly_argv, which builds the argv that actually executes. So a POSIX operand containing a literal \\?\ or \\ prefix would be rewritten there too. In practice no real POSIX command looks like that, and keeping it platform-independent is what lets the classifier tests run on every runner, which is worth more than the edge case. Flagging it only so the tradeoff is written down.

Nice, well-tested fix — the windows_verbatim_and_drive_operands_survive_posix_split table covering \\?\, drive, UNC, and device spellings is exactly the right shape.

@Hmbown

Hmbown commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Follow-up: every check is now greenTest (macos-latest) came back pass (42m35s), so the full matrix plus Version drift, Lint, Safety gate and the rest are all passing. mergeStateStatus: CLEAN.

For the record on why this one matters more than its diff size suggests — the two gates it fixes are currently red on five other open PRs (#5592, #5593, #5594, #5611, and #5609's Windows lane), all inheriting the breakage from main rather than causing it. This PR is the single change that clears both.

@aboimpinto

Copy link
Copy Markdown
Contributor Author

CI is fully green, including the previously failing Test (windows-latest) job (19m53s). The fix normalizes Windows verbatim/backslash path spellings at every judging site (both command_safety classifiers and both shell.rs split sites), so:

  • tools::shell::tests::readonly_operands_are_workspace_bounded_and_symlink_aware
  • tools::subagent::tests::read_only_inspection_roles_execute_pwd_and_absolute_git_log

Once merged, FEAT-019 (PR #5609) will be rebased and its Windows job should go green as well. Ready for review/merge.

Paulo Aboim Pinto

@Hmbown
Hmbown merged commit 7f26bb9 into Hmbown:main Aug 25, 2026
22 checks passed
Hmbown pushed a commit that referenced this pull request Aug 25, 2026
#5610)

Windows canonicalizes operands to \\?\-prefixed verbatim paths; that
prefix is Win32 syntax, not a glob, but its '?' tripped both readonly
classifiers' metacharacter gates and the POSIX tokenizer ate the
backslashes. normalize_windows_command_paths strips the prefix for
CLASSIFICATION ONLY (drive-letter and UNC forms both handled, quoting
preserved, mid-token '?' left alone so wildcards still reject); the
executed argv is never rewritten — real splitting stays in
split_command_windows_style (#5595). Adapted from PR #5610, whose
tools/shell.rs hunks are superseded on this branch.

Co-Authored-By: Grok 4.6 <noreply@anthropic.com>
Hmbown pushed a commit that referenced this pull request Aug 29, 2026
…ntities

Twenty external contributions landed after the v0.9.11 band closed and none
were credited: the newest band in docs/CONTRIBUTORS.md was still v0.9.11 while
main shipped 0.9.12 work. Adds the v0.9.12 band covering all twenty (#5591,
#5599, #5604, #5610, #5613, #5621-#5624, #5629, #5650, #5657, #5669, #5682,
#5683, #5685, #5687, #5688, #5689, #5692) and demotes v0.9.11 so only the
newest band stays expanded.

AUTHOR_MAP gains numeric-noreply entries for musichen, M-Maciej, and serephus
so future harvested credit is graph-mappable, plus a comment recording that
wangfengcsu@qq.com (21 commits on main via #704) has no resolved login yet.

AUTHOR_MAP is a project convention consumed by scripts/check-coauthor-trailers.py
for new commits; GitHub does not read it, so it grants no retroactive
contribution-graph credit for history already on main.

No-Issue: contributor credit hygiene; no issue tracks it
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
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.

2 participants