#1512

Bug: Shell snapshot misses edits to $ZDOTDIR/.zshrc when ZDOTDIR isn’t exported

Openbmbtranopened 15d ago

Issue details

If ~/.zshenv sets ZDOTDIR without export, fx never notices edits to $ZDOTDIR/.zshrc. It keeps replaying the snapshot it captured at startup, so changes made through fx's own shell tool don't show up in later commands. ${ZDOTDIR} is also unset inside the commands fx runs, even though a real login zsh has it set

This is separate from the known limitation in #1329 (it covers edits to a file your startup files source. Here $ZDOTDIR/.zshrc is a top-level startup file that zsh reads directly and it still isn't watched

Repro

Needs zsh, since print -r is a zsh builtin.

d=$(mktemp -d); mkdir $d/zd
print -r -- "unset ZDOTDIR; ZDOTDIR=$d/zd" > $d/.zshenv
print -r -- 'export MARK=v1' > $d/zd/.zshrc
cd $d

# zsh itself handles the unexported form fine:
ZDOTDIR=$d zsh -lic 'echo "param=$ZDOTDIR mark=$MARK"'
# => param=/tmp/.../zd mark=v1

ZDOTDIR=$d fx ask --auto --no-save --json "Run these as three separate shell tool calls. Wait for each result before making the next call. Call 1: echo \$MARK  Call 2: echo 'export MARK=v2' > $d/zd/.zshrc  Call 3: echo \$MARK"

Call 3 should print v2. On fx 0.0.15 (Ubuntu 24.04, zsh 5.9, gpt-6-luna):

Running echo $MARK
v1
Running echo 'export MARK=v2' > ./zd/.zshrc
Running echo $MARK
v1

The model rewrote call 2 to a relative path, but cwd is $d so it writes to the same file.

The same run in a fresh temp dir with export ZDOTDIR=$d/zd prints v2 on call 3. The export is the only difference between the two runs.

Cause

The capture script collects variables with env, which only prints exported ones (src/core/terminal/shell_resolver.zig:331):

\\/usr/bin/env -0

ZDOTDIR is picked up from that list (src/core/terminal/shell_snapshot.zig:708):

if (std.mem.eql(u8, key, "ZDOTDIR") and value.len != 0) {
    generation.zdotdir = try alloc.dupe(u8, value);
}

and that value decides which directories get watched (src/core/terminal/shell_snapshot.zig:658):

const dirs = [_]?[]const u8{ io_mod.getenv("ZDOTDIR"), io_mod.homeDir(), snapshot_zdotdir };

An unexported ZDOTDIR shows up in neither env -0 nor getenv, so the directory holding the startup files never makes it into that list. Both lines came in with #1137.

Does anyone actually skip the export?

ZDOTDIR is a shell parameter, so zsh reads $ZDOTDIR/.zshrc either way. Kitty's zsh integration carries a comment saying it normally shouldn't be exported, and has code for the case where it is. Ghostty's version started as a copy of Kitty's. Most dotfiles I looked at do export it, so this setup is a minority, but it works fine under zsh.

Possible fix

Emit ZDOTDIR explicitly from the capture script instead of relying on env. Happy to put up a PR but would love to know if you'd prefer that narrow fix or something more general for shell parameters.

Tested on commit d72b46594ca6 (fx 0.0.15), no local changes

Sensitive information

  • I reviewed the information above and removed secrets and other sensitive data.

Activity

0

No activity yet.