Skip to content

Keep Escaped Backslash Pairs Before a Line Break in the Write Guard - #3368

Merged
ptr727 merged 2 commits into
developfrom
feature/guard-backslash-pair
Oct 22, 2026
Merged

ptr727 merged 2 commits into
developfrom
feature/guard-backslash-pair

Conversation

@ptr727

@ptr727 ptr727 commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the first three findings of #3358 in the write guard's backslash-newline narrowing.

  • Escaped backslash pairs before a kept newline. A shell running a kept quoted span (a single-quoted shell body, or a quoted heredoc fed to a shell) reads each escaped backslash pair before a line break as one literal backslash on the word. Every reading _line_break_reading built dropped the whole run instead, so bash -c 'git push origin main\\<newline>echo hi' was read as a push of main, where the inner shell pushes main\. The readings now keep the pairs as script text, so a shell reading them still sees one literal backslash per pair. A space parts the pairs from the line break, so no reading holds a backslash-newline that classify would read again. The odd-run line-break reading keeps its pairs the same way. Outside a kept span nothing changes, and an escaped backslash before a line break is still denied there.
  • _literal_span_kept docstring. The summary line no longer reads as if every kept span is read as its own scan places it. A span naming no git, gh, or push is kept whatever its scan found, and the word test now names an escaped part and an escaped word break before the word.
  • README requirement 4. States the word test (no other quoted, escaped, or expanded part, and no escaped word break just before it) and the names test (git, gh, or a push, read as written, joined, and with line breaks), and says every reading keeps the escaped pairs.

Out of scope: #3358's fourth finding, a Markdown body naming git or gh across a further quoting level. It goes to #3354's single context walker and is not attempted here.

Verification

  • python3 host-setup/agent-safety/claude/gh-write-guard.py --selftest: SELFTEST PASS, with new _CASES entries: four allows, plus control denies for the line after a pair and for a pair ending a comment in a shell body.
  • Revert proof: with only the _line_break_reading change reverted and the new cases kept, the self-test fails on exactly the four new allow cases (deny where allow is wanted). The control denies pass either way.
  • Local strict review: two passes. Pass 1 raised a docstring overclaim (a joined odd run before an empty line leaves pairs before a newline, which classify re-reads and denies correctly) and a missing comment-ending test. Both are fixed in the second commit, and pass 2 raised nothing.
  • ruff format and check, mypy, prose_lint.py --diff (default and --check sentence-length), the eol gate, and markdownlint on the README are clean.

Develop-versus-branch differential

Every command classified by develop (075ec57a) and by this branch with the self-test's pins:

Corpus Commands Flips
develop's own self-test case lists 1618 0
constructed probes 280 30, all deny -> allow

The probes are 40 templates, each with a backslash run of 0 to 6 before a newline. They cover even and odd runs, outside quotes, inside a single-quoted bash -c / sh -c body, inside a double-quoted body, in a quoted heredoc fed to bash, in a $(...) substitution, in gh pr comment / gh pr create bodies, and next to git, gh, push, cd, -C, --no-verify, --force, and a comment.

Every flip is intended. Each has a run of 2 to 6 backslashes, so at least one escaped pair, before the newline in a kept span:

  • bash -c 'git push origin main<run><newline>echo hi', bash -c 'git push origin main<run><newline>', the same in a quoted heredoc fed to bash, and inside echo "$(...)" (20 flips). The inner shell's refspec is main\ (even run, followed by a line break) or main\echo (odd run, joined). A ref name cannot hold a backslash, so git refuses either and no push of main runs.
  • bash -c 'git push origin HEAD:main<run><newline>' (5 flips). The destination is main\, which git refuses for the same reason.
  • bash -c 'gh<run><newline>pr merge 5 --admin' (5 flips). The inner shell runs a command named gh\ (even run) or gh\pr (odd run joined), not gh.

The single-backslash run (a plain continuation) and the plain newline flip nothing, and the control shapes that must stay denied, such as a later line pushing main, a cd before a push, -C split from push, and --no-verify on the next line, stay denied.

Refs #3358

🤖 Generated with Claude Code

ptr727 and others added 2 commits October 22, 2026 13:29
A shell running a kept quoted span reads each escaped backslash pair
before a line break as one literal backslash, so the word keeps it.
Every reading dropped the whole run, which read a push of a word ending
in a backslash as a push of the word without it. The readings now keep
the pairs, parted from the line break by a space so no reading holds a
backslash-newline to read again.

Also correct the README and the _literal_span_kept docstring, which
understated the word test and the names test, and read as if every
kept span is read as its own scan places it.

Refs #3358

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The docstring said no reading holds a backslash-newline, but a joined
odd run followed by an empty line leaves its pairs before a newline,
which classify reads again and denies as the shell would. State only
that the break a reading adds is not read again, and add controls for
an escaped pair ending a comment in a shell body.

Refs #3358

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings October 22, 2026 20:40
@ptr727

ptr727 commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head b982f88f47095e65ad4a75bfd421bfeb31f8b78e, the content this pull request carries at that commit against develop, and it recorded 0 findings.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 47 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 075ec57 and b982f88.

📒 Files selected for processing: 2
  • host-setup/agent-safety/README.md
  • host-setup/agent-safety/claude/gh-write-guard.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No unresolved review issues were identified, and the documented behavior and regression coverage are aligned.

No open issues 🎉

What changed in this PR

This pull request updates the write guard to preserve escaped backslash pairs when rereading kept quoted shell text, while aligning documentation and regression coverage.

Changes:

  • Preserve escaped backslash pairs across generated line-break readings.
  • Add allow and deny self-tests for shell bodies and quoted heredocs.
  • Clarify the README requirement and _literal_span_kept documentation.
File Description
host-setup/​agent-safety/​README.md Documents the expanded kept-span, command-name, and escaped-pair rules.
host-setup/​agent-safety/​claude/​gh-write-guard.py Updates line-break readings, documentation, and regression/control test cases.

Review effort: Lite

@codecov

codecov Bot commented Oct 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (develop@075ec57). Learn more about missing BASE report.

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #3368   +/-   ##
==========================================
  Coverage           ?   81.32%           
==========================================
  Files              ?       18           
  Lines              ?    10047           
  Branches           ?        0           
==========================================
  Hits               ?     8171           
  Misses             ?     1876           
  Partials           ?        0           
Flag Coverage Δ
python-3.13 81.32% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ptr727

ptr727 commented Oct 22, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ptr727
ptr727 merged commit 4f16a3e into develop Oct 22, 2026
14 checks passed
@ptr727
ptr727 deleted the feature/guard-backslash-pair branch October 22, 2026 21:11
ptr727 added a commit that referenced this pull request Oct 22, 2026
…dows Self-Test Skip, and Subprocess Failure Rule to Main (#3359)

## Summary

- Skip the invalid-byte audit self-test probe when Windows Git cannot
add the name, so the self-test no longer fails on a host that cannot
represent the path (#3350). The skip fires only on that one add, after a
control file's add has proven git works (#3360).
- Read a backslash-newline in the write guard by its quoting: inside
single quotes and quoted heredoc data it is text, and a continuation
after a shell operator is no word split (#3357).
- Keep each escaped backslash pair before a kept line break in the
guard's readings, as the shell does, and state the kept-span word and
names tests in full in the README and docstring (#3368).
- Add a `CODESTYLE.md` General rule: classify a subprocess failure by
which command failed and the status code it reports, never by the
wording of its message (#3366).

Closes #3338
Closes #3358
Closes #3363

Refs #3344, which stays open because only part of it is fixed here.
#3358's Markdown-body finding moves to #3354.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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