fix(exec): isolate alias capture from rc-file stdout#418
Merged
Conversation
Capture sourced the user's rc file via `$SHELL -i -c "alias -p; echo SENTINEL; declare -f"` and split the shell's stdout on a sentinel. Anything the rc-file printed to stdout — p10k instant prompt, plugin status banners, "welcome" echoes, fortune cookies — was indistinguishable from real `alias` output and got persisted into the cached snapshot, then re-injected into every subsequent invocation. The slow-path fallback was also `-i -c CMD`, so when capture failed there was no escape: `daft exec` became unusable. Capture now writes alias and function output to dedicated temp files via env vars (`__DAFT_ALIAS_OUT`, `__DAFT_FN_OUT`); the shell's own stdout/stderr is discarded entirely. A 10s deadline guards against rc-files that hang. A `# daft-alias-cache v2` header auto-invalidates already-poisoned caches on disk without requiring `--refresh-aliases`. Captured content is sanity-checked before persistence. When no cache is available the fallback is now a pristine `$SHELL -c CMD` instead of `$SHELL -i -c CMD`. Aliases won't resolve in the fallback, but the user's command runs — strictly better than today's "completely useless" failure for users whose rc files break in non-interactive contexts. `run_exec_commands` (the legacy `-x` path on `daft clone` / `daft init` / `daft checkout` / `daft go` / `daft start`) shared the same `-i -c` vulnerability. It now reuses `build_command` so the cache + rc-less fallback covers that surface too. `DAFT_EXEC_DEBUG=1` emits stderr lines for chosen execution mode, capture failures, validation rejections, and cache hits — the user who reported this had no logs, so the fix ships its own. A unit test simulating a polluting `.bashrc` confirms the cache no longer absorbs rc-stdout, and a YAML manual scenario covers the end-to-end flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 27, 2026
Merged
Closed
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.
Summary
daft exec(and the-xflag ondaft clone/daft init/daft checkout/daft go/daft start) became unusable for users whose.zshrc/.bashrcprinted anything to stdout during rc evaluation — p10k instant prompt, plugin status banners, "welcome" echoes, fortune cookies. Their stdout output was indistinguishable from realaliasoutput and got persisted into the cached snapshot, then re-injected into every subsequent invocation.alias/typeset -foutput to dedicated temp files inside the spawned shell (paths passed via env vars). The shell's own stdout/stderr is discarded entirely, so rc-file noise can't corrupt the snapshot. A 10s deadline kills hangs fromexec tmux-style rc-files. A# daft-alias-cache v2header auto-invalidates already-poisoned caches on disk without requiring--refresh-aliases.$SHELL -c CMDinstead of$SHELL -i -c CMD. Aliases won't resolve in the fallback, but the user's command runs — strictly better than today's "completely useless" failure for users whose rc files break in non-interactive contexts.run_exec_commands(the legacy-xpath on worktree-creation flows) now reuses the samebuild_command, so the cache + rc-less fallback covers both surfaces.DAFT_EXEC_DEBUG=1emits stderr lines for chosen execution mode, capture failures, validation rejections, and cache hits — the reporter had no logs, so the fix ships its own.Test plan
.bashrcpreviously corruptedalias_lineswithPOLLUTION_BEFORE_PROMPT_INITetc.; now passes after the FD-isolated capture.tests/manual/scenarios/worktree-exec/polluted-rcfile.ymlcovers the end-to-end flow..bashrc→ fast-path resolves alias cleanly.SHELL=/bin/sh→ rc-less fallback, command runs.daft init -x 'alias_name'with polluting bashrc → alias resolves through cache.mise run fmt:check,mise run clippy,cargo test --lib core::worktree::execall clean (47 tests pass).Notes for users
DAFT_EXEC_DEBUG=1and the stderr trace will show whether capture succeeded, which mode was chosen, and the constructed shell args.🤖 Generated with Claude Code