Skip to content

fix(sessions): stop a duplicate inheriting a deleted directory - #171

Merged
kipavy merged 1 commit into
devfrom
fix/cwd-probe-deleted-dir
Aug 24, 2026
Merged

fix(sessions): stop a duplicate inheriting a deleted directory#171
kipavy merged 1 commit into
devfrom
fix/cwd-probe-deleted-dir

Conversation

@kipavy

@kipavy kipavy commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

The bug

Duplicating a session opened it in the home directory instead of the source session's directory. Reported after it had been working, with the SFTP panel's follow-cwd still tracking correctly on the same host.

Root cause

Found in the process table of a live session. Its screen wrapper carried:

cd '/home/ubuntu/fourretout/voltius-dev/voltius/.claude/worktrees/pr-42-key-sync (deleted)' 2>/dev/null;

The cd fired, at a path ending in the literal string (deleted). It failed, 2>/dev/null ate the error, the shell stayed in $HOME.

cwd_probe_command descends screen's process tree and reads /proc/<pid>/cwd of the deepest process. Linux resolves that link to <path> (deleted) once the directory is unlinked, and the probe accepted it raw. From there it flowed through the ssh-cwd-<id> event into the cwd store, and out again as the initial_cwd a duplicate cds into.

Nothing in the duplicate feature changed — the failure needed only a long-lived program (an editor, an agent) pinned to a directory that was later removed. Here that was a merged PR's worktree.

The fix

cwd_probe_command — only count a candidate whose directory still exists:

[ -n "$cur" ] && [ -d "$cur" ] && cwd=$cur

[ -d "/path (deleted)" ] is false, so dead paths are rejected without a stripping heuristic, and the walk falls back to the last descendant with a live cwd: the shell. Dropping the descent's in-script comments also shrinks the base64 payload the dropbear MAX_EXEC_COMMAND_LEN budget has to fit.

Ingestionis_live_probe_cwd() rejects non-absolute output and the (deleted) form where the poll loop reads the probe, for hosts whose readlink behaves differently. Single producer, single consumer, so there is one guard rather than two copies.

Tests

descent_ignores_a_descendant_whose_directory_was_removed runs the descent against a real three-level process tree (start → shell in livesleep in doomed), waits until the walk reaches the deepest process, removes doomed under it, and asserts the descent returns live. Linux-gated, since the probe is /proc-only.

Verified red without the fix:

left:  "/tmp/voltius-descent-933406/doomed (deleted)"
right: "/tmp/voltius-descent-933406/live"

probe_cwd_must_be_an_absolute_live_path covers the predicate directly.

cargo test --lib shell_integration:: — 21 passed, 0 failed. cargo fmt --check clean. The 3 commands::plugins:: failures in a full --lib run are unrelated: SEEDED_PLUGINS is empty without built plugin bundles, and they fail identically on clean dev.

Out of scope

A shell genuinely sitting in a deleted directory still puts the duplicate in $HOME with no explanation, since the remote cd discards its error.

The persistent-session cwd probe descends screen's process tree and reads
/proc/<pid>/cwd of the deepest process. The kernel resolves that link to
"<path> (deleted)" once the directory is unlinked, and the probe took it
verbatim: it reached the cwd store, the SFTP panel's follow-cwd, and the
initial_cwd a duplicated session cds into. That cd can never succeed, and
its error is discarded, so duplicating a session whose foreground program
was pinned to a since-removed directory (a deleted worktree, say) landed
silently in the home directory instead.

Only count a candidate whose directory still exists, so the walk falls
back to the last descendant with a live cwd -- the shell itself -- and
refuse the "(deleted)" form again where the probe output is ingested, for
hosts whose readlink behaves differently.

The descent moves into a constant so the test can run it against a real
three-level process tree with a directory removed underneath it.
@kipavy
kipavy merged commit 2f20a15 into dev Aug 24, 2026
4 checks passed
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.

1 participant