Give the sandbox a UTF-8 locale

A sandbox image ships without a locale, leaving LC_CTYPE at POSIX. Every
multibyte glyph then degrades to a placeholder, so Nerd Font icons in the
editor render as underscores and bash printf emits \uXXXX escapes literally.

The locale is probed for usability rather than matched by name, because
locale -a spells C.UTF-8 as C.utf8 on glibc and a name match would silently
find nothing. LANG alone is set, leaving individual categories overridable.
This commit is contained in:
2026-08-03 15:26:41 -05:00
parent 14165503d5
commit 53db7df812
2 changed files with 48 additions and 2 deletions
+35
View File
@@ -1055,6 +1055,39 @@ EOF
' '
} }
# A sandbox image ships without a locale, which leaves LC_CTYPE at POSIX. Every
# multibyte glyph then degrades to a placeholder: Nerd Font icons in the editor
# render as underscores, and bash printf emits \uXXXX escapes literally. LANG
# alone is enough, and leaves a user free to override individual categories.
install_sandbox_locale() {
# shellcheck disable=SC2016
sbx exec "$SANDBOX_NAME" bash -c '
persistent=/etc/sandbox-persistent.sh
marker="# BEGIN ai-sbx locale"
if grep -Fq "$marker" "$persistent" 2>/dev/null; then
exit 0
fi
# locale -a spells these inconsistently across distributions, so probe
# each one for usability rather than matching its name.
for candidate in C.UTF-8 en_US.UTF-8; do
if LC_ALL="$candidate" locale >/dev/null 2>&1; then
chosen="$candidate"
break
fi
done
[[ -n "${chosen:-}" ]] || exit 0
cat >>"$persistent" <<EOF
# BEGIN ai-sbx locale
export LANG=$chosen
# END ai-sbx locale
EOF
' </dev/null >/dev/null 2>&1 || true
}
validate_launch_mode() { validate_launch_mode() {
case "$1" in case "$1" in
agent | tmux) ;; agent | tmux) ;;
@@ -1427,6 +1460,8 @@ run_command() {
# runs every time rather than only at setup. # runs every time rather than only at setup.
install_sandbox_mise install_sandbox_mise
install_sandbox_locale
if [[ "$launch" == tmux ]]; then if [[ "$launch" == tmux ]]; then
install_sandbox_workspace install_sandbox_workspace
+13 -2
View File
@@ -47,8 +47,12 @@ dispatch() {
cat "$SBX_LOG" cat "$SBX_LOG"
} }
[[ "$DEFAULT_LAUNCH" == agent ]] || (
fail "AI_SBX_LAUNCH unset should default to agent, got: $DEFAULT_LAUNCH" unset AI_SBX_LAUNCH
# shellcheck source=tasks/ai/sbx
source "$TASK"
[[ "$DEFAULT_LAUNCH" == agent ]]
) || fail "AI_SBX_LAUNCH unset should default to agent"
( (
AI_SBX_LAUNCH=tmux AI_SBX_LAUNCH=tmux
@@ -87,6 +91,13 @@ DEFAULT_LAUNCH=agent
[[ "$(dispatch -- --launch tmux)" == *"run ai-test -- --launch tmux"* ]] || [[ "$(dispatch -- --launch tmux)" == *"run ai-test -- --launch tmux"* ]] ||
fail "--launch after -- belongs to the agent, not to the task" fail "--launch after -- belongs to the agent, not to the task"
DEFAULT_LAUNCH=agent
locale_dispatch="$(dispatch)"
[[ "$locale_dispatch" == *"BEGIN ai-sbx locale"* ]] ||
fail "run should install a UTF-8 locale, or Nerd Font glyphs render as placeholders: $locale_dispatch"
[[ "$locale_dispatch" == *"LC_ALL=\"\$candidate\" locale"* ]] ||
fail "the locale must be probed for usability, not matched by name against locale -a"
if (validate_launch_mode bogus) 2>/dev/null; then if (validate_launch_mode bogus) 2>/dev/null; then
fail "an unknown launch mode was accepted" fail "an unknown launch mode was accepted"
fi fi