From 5c08bfc8a840dfd4258dd7a8a725c34d3f8f4469 Mon Sep 17 00:00:00 2001 From: Joakim Persson Date: Wed, 17 Jun 2026 17:22:30 +0200 Subject: [PATCH] fix(shell): don't export DEVBOX_HIST_SET so nested shells flush history The history-flush guard was exported, so it leaked into child processes. Any nested shell -- crucially each tmux pane (which inherits the tmux server's env) -- then saw the guard already set and skipped installing 'history -a' in PROMPT_COMMAND. Those shells only persisted history on a clean exit, so abrupt termination (docker stop, tmux kill-server, SIGKILL) silently lost their in-memory history. zoxide was less affected (its hook is installed unguarded and writes immediately). Make the guard shell-local (drop 'export') so every new interactive shell re-installs its own per-prompt flush. Add a recreate-sanity-check assertion that a nested login shell still wires up 'history -a'. Storage was never the issue: ~/.cache/bash (devbox-shell-history) and ~/.local/share/zoxide (devbox-zoxide) are both persistent named volumes. --- rootfs/home/developer/.bash_aliases | 9 ++++++++- scripts/recreate-sanity-check.sh | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/rootfs/home/developer/.bash_aliases b/rootfs/home/developer/.bash_aliases index 65d7148..c11ab13 100644 --- a/rootfs/home/developer/.bash_aliases +++ b/rootfs/home/developer/.bash_aliases @@ -89,9 +89,16 @@ fi # we append with a newline separator to avoid the ';;' parse error # described at the top of this file. Guarded so repeated sourcing # (e.g. `exec bash`) doesn't stack duplicates. +# +# The guard MUST stay shell-local (NOT exported): if it leaks into child +# processes, every nested shell -- crucially each tmux pane, which inherits +# the tmux server's env -- skips installing `history -a` and only persists +# history on a clean exit. Abrupt termination (docker stop, tmux kill-server, +# SIGKILL) then loses that shell's in-memory history. Keeping it unexported +# means each new interactive shell re-installs its own per-prompt flush. if [ -z "${DEVBOX_HIST_SET:-}" ]; then PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a" - export DEVBOX_HIST_SET=1 + DEVBOX_HIST_SET=1 fi # ── Prompt: show [opencode-devbox] tag so it's obvious you're in the container diff --git a/scripts/recreate-sanity-check.sh b/scripts/recreate-sanity-check.sh index e7bfb6a..41dff1d 100755 --- a/scripts/recreate-sanity-check.sh +++ b/scripts/recreate-sanity-check.sh @@ -222,6 +222,16 @@ else fail "~/.bash_aliases missing" fi +# History flush must survive shell nesting. The DEVBOX_HIST_SET guard must NOT +# be exported: if it leaks into child processes, nested shells (esp. tmux +# panes) skip installing `history -a` and lose in-memory history on abrupt +# termination. Assert a child login shell still wires up the per-prompt flush. +if bash -lic 'bash -lic "case \"\$PROMPT_COMMAND\" in *\"history -a\"*) exit 0;; *) exit 1;; esac"' /dev/null 2>&1; then + pass "nested shell installs 'history -a' (DEVBOX_HIST_SET not exported)" +else + fail "nested shell missing 'history -a' — DEVBOX_HIST_SET leaking to children?" +fi + if [ -f "$HOME/.inputrc" ]; then pass "~/.inputrc exists" else