Skip to content

action+skills: workaround Linux bwrap shell-quote bang bug, drop now-unneeded guidance - #504

Merged
max-sixty merged 3 commits into
mainfrom
backticks
May 15, 2026
Merged

action+skills: workaround Linux bwrap shell-quote bang bug, drop now-unneeded guidance#504
max-sixty merged 3 commits into
mainfrom
backticks

Conversation

@max-sixty

@max-sixty max-sixty commented May 15, 2026

Copy link
Copy Markdown
Owner

Bug

Linux Claude Code v2.1.142's bubblewrap-backed subprocess sandbox wraps bash commands using the legacy shell-quote quote function with the regex /(["\\$!])/g, which escapes !to!in any argv token that mixes'with whitespace. That landed!=injqfilters,assert!()in code references,feat(api)!:in conventional-commit titles, etc. The same binary contains a **fixed**'''-wrap quoting path for the macOS sandbox-exec` route — both paths coexist, only the Linux-bwrap path still uses the buggy quoter. See anthropics/claude-code#35701 (closed as stale; multiple duplicates closed since).

Fix

  1. action.yaml: set CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=0 on the claude-code-action invocation. This disables the bwrap sandbox and routes bash through the fixed '\''-wrap path. Trade-off: loses subprocess env isolation. Applied at the composite-action level, so all max-sixty/tend@v1 consumers pick it up automatically once tagged.

  2. Skills: with the bug worked around, the bang-trap guidance can go. Removed across triage, notifications, review-runs, review-reviewers, and running-in-ci:

    • "Don't use ! in comment body" / "use Write tool for bodies with !"
    • != rewrite warnings in jq recipes (and the awkward (.status == "completed") | not rephrase)
    • --title-file and git commit -F workarounds
    • || not if ! rationale comments
    • <<'EOF'/<<EOF interpolation footgun warning
  3. Body composition: standardized on Write tool + --body-file for substantial PR/comment/gist bodies. Removed the inline gh ... --body "$(cat <<'EOF' ...)" heredoc patterns from review-reviewers's gist seed and step summary and the line-wrap example in running-in-ci. Write-tool composition is reviewable, sandbox-independent, and bypasses the shell-quoting layer entirely.

Kept on purpose

Validation posture

Workaround is empirically targeted: the bug reproduces in CI (bwrap sandbox active), does not reproduce locally (sandbox-exec on the fixed path), and the env-var docstring inside the binary explicitly documents the opt-out. If a regression slips, review-reviewers's grep -nP '\\!' / grep -nP '\\\`' scans against the bot's own output fire on the next run. Easy revert via git revert <commit> if anything misbehaves.

…bodies

The Claude Code Bash-tool `!`→`\!` rewrite no longer reproduces. Removed
the workaround guidance across triage, review-reviewers, review-runs,
running-in-ci, and notifications. Standardized PR/comment/gist body
composition on the Write tool + --body-file. Kept the corruption-scan
tripwires in review-reviewers so a regression is detectable.

Co-Authored-By: Claude <noreply@anthropic.com>

@tend-agent tend-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The premise — "the Bash-tool !\! rewrite no longer reproduces" — does not hold in this CI environment. The preprocessor is still active, so the workarounds being removed are still load-bearing.

Repro from this very review run:

$ echo "hello!"
hello\!

$ echo '[{"status":"completed","id":1},{"status":"in_progress","id":2}]' \
    | jq '.[] | select(.status != "completed") | .id'
jq: error: syntax error, unexpected INVALID_CHARACTER, expecting ';' or ')' (Unix shell quoting issues?) at <top-level>, line 1:
.[] | select(.status \!= "completed") | .id
jq: 1 compile error

$ cat << 'EOF'
> Thanks for reporting this!
> EOF
Thanks for reporting this\!

Concrete consequences if this lands:

  1. The new select(.status != "completed") line in running-in-ci (the rerun-poll recipe) will fail with Invalid character the next time tend-ci-fix reruns failed jobs — exactly the failure mode the removed comment warned about. Inline suggestion below.
  2. Bot greetings ending in !, code references like assert!() / format!() / panic!() / eprintln!(), and conventional-commits breaking-change titles like feat(api)!: will start shipping as \! again. The corruption-scan tripwires kept in review-reviewers will catch the regression after the fact, but the per-skill author guidance that prevented the corruption upfront is gone.
  3. Single-quoted heredoc bodies with ! in them will ship corrupted (last block above).

The good direction here — "compose bodies with the Write tool, post via --body-file" — does still avoid the corruption (verified: a Write-tool-authored file is bytes-perfect, no preprocessor in that path). So the broader simplification is sound; it's the assumption that bash heredocs and inline != are now safe that breaks.

Suggest one of:

  • Hold this PR until the upstream fix is verified in the CI image tend-agent runs in (paste a gh workflow run link showing echo "hi!" printing hi! before merging), or
  • Keep the per-skill bang-trap callouts and the ||-not-if ! reminders, and limit this PR to the genuinely-safe parts (the --body-file standardization, the corruption-scan recipe simplification, the Write-tool-for-step-summary rewrite). Just revert the != swap and the four "Use || not if !" / "Bash-tool preprocessor" deletions in notifications, review-runs, review-reviewers (the corruption-scan jq filter), and triage.

Separately, two smaller observations on the cleanup direction itself (non-blocking):

  • The new pseudo-code blocks in review-reviewers (gist seed, step summary) embed instructions like # Use the Write tool to author … as bash comments inside a fenced bash block. It works, but reads ambiguously — a model could plausibly try to execute the block as bash and end up with an empty /tmp/gist-seed/findings.md. Prose between two real code blocks would be unambiguous.
  • The shortened **GitHub URLs** paragraph drops the previous explicit guidance that the Write tool doesn't expand env vars (and the OWNER_REPO placeholder + sed -i recipe). Without that, a body authored with https://github.com/${GITHUB_REPOSITORY}/issues/123 ships the literal ${GITHUB_REPOSITORY}, not the value. "Read from the environment" implies the right action but doesn't spell it out.

Comment thread plugins/tend-ci-runner/skills/running-in-ci/SKILL.md
max-sixty and others added 2 commits May 15, 2026 13:25
…ng bug

Linux Claude Code v2.1.142's bwrap-wrapping path uses the legacy
shell-quote quote() (/(["\\$`!])/g), which escapes ! to \! in any argv
token containing both ' and whitespace. Disabling
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB routes bash through the fixed
'\''-wrap path at the cost of subprocess env isolation. macOS uses
sandbox-exec on an already-fixed path, so this is Linux-only. See
anthropics/claude-code#35701.

Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts:
#	plugins/tend-ci-runner/skills/running-in-ci/SKILL.md
@max-sixty max-sixty changed the title skills: drop bang/backtick escape workarounds, prefer Write tool for bodies action+skills: workaround Linux bwrap shell-quote bang bug, drop now-unneeded guidance May 15, 2026
@max-sixty
max-sixty merged commit b611e99 into main May 15, 2026
6 checks passed
@max-sixty
max-sixty deleted the backticks branch May 15, 2026 21:21
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