Warn when config.shell is not a shell mandelbot can drive - #181
Open
phil-kremidas-unitedmasters wants to merge 1 commit into
Open
Warn when config.shell is not a shell mandelbot can drive#181phil-kremidas-unitedmasters wants to merge 1 commit into
phil-kremidas-unitedmasters wants to merge 1 commit into
Conversation
astex
force-pushed
the
validate-shell-config
branch
from
August 3, 2026 18:32
36f7e3a to
e155b95
Compare
A `shell` set to a wrapper script silently broke every claude tab: the claude launcher passes `-l -i -c "<cd project dir> && exec claude ..."`, and a script ignores those four arguments entirely, running its own body instead. Tabs opened in whatever directory the script cd'd to, with no diagnostic anywhere. Classify `shell` at config-load time and surface the result as a toast that stays until dismissed (plus a stderr line). The check never refuses to start — an unusual-but-working shell should still boot, just loudly. Detection is structural, in this order: - a shebang means the file is a script, whatever the interpreter, and a script always drops the arguments mandelbot passes it - basename in a POSIX-shell allowlist is fine, at any path - fish/csh/tcsh/nu/... are real shells that don't honor `-l -i -c` - a script-ish extension, or an unrecognized name, warns more softly Also fix two crashes on an empty `shell`: the unchecked `shell_parts[0]` index in the claude path and the `.expect` in the plain-shell path both panicked the tab thread. Both now fall back to `default_shell()`, and config load rewrites an empty value up front. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
astex
force-pushed
the
validate-shell-config
branch
from
August 13, 2026 16:29
e155b95 to
66448cc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
A user set
shellin~/.mandelbot/config.jsonto a custom.shwrapper. Every project tab and the home tab then opened claude in the same hardcoded directory, silently.The claude launcher in
src/tab/stream.rsbuildscd '<project dir>' && exec claude ...and hands it to the shell as-l -i -c <command>. A script ignores all four arguments and just runs its own body — including its owncd. The carefully constructed command is thrown on the floor with no diagnostic anywhere.Detection
Structural checks only, in
config.rs, ordered most-decisive first:.shextension.sh/bash/zsh/dash/ksh/… at any path, so/opt/homebrew/bin/bashand/nix/store/…/bin/bashare fine.fish,csh,tcsh,nu,pwsh, … are real shells that genuinely don't honor-l -i -cthis way. Distinct message: not "this is a script".Why not a probe. Running
<shell> -l -i -c 'exit 7'at startup is empirical, but it costs a subprocess on every launch and a pathological wrapper (one that reads stdin, or blocks) hangs the app before the window appears. The shebang read gets the same evidence for the realistic failure at the cost of one 128-byte file read.Why not an allowlist alone. It wrongly rejects a valid shell at an unusual path, and it can't tell
~/bin/myshell-the-binary from~/bin/myshell-the-wrapper. The shebang can.Surfacing
A toast, via the existing
src/toast.rsmachinery, seeded inApp::boot— mandelbot is a GUI app and this exact bug's signature is "looks like a mandelbot bug, nothing on stderr the user ever sees". Deliberately not auto-dismissed after the usual 10s: it stays until closed. Also printed to stderr once.The check never refuses to start. An unusual-but-working
shellstill boots, just loudly.Empty-shell panics
Both crash sites are fixed:
stream.rsclaude path indexedshell_parts[0]uncheckedstream.rsplain-shell path used.expect("shell config must not be empty")Both now fall back to
default_shell()with a warning, andConfig::loadrewrites an empty value up front so neither should fire in practice.Tests
13 new unit tests over the pure
check_shell(shell, shebang)function — allowlist at odd paths, shell-with-arguments, empty/whitespace, shebang beating a shell-like filename, an ELF first line not being a shebang, case-insensitive extensions,#!/usr/bin/env bashresolving tobash, and every warning naming~/.mandelbot/config.json.cargo buildandcargo testpass — 91 tests, up from 78.Verified by hand against a
.shwrapper, an emptyshell, and/bin/zsh(silent).🤖 Generated with Claude Code