Repository navigation
fix(terminal-history): drop an inherited Orca fish_history so nested Orca panes stop merging worktree histories (STA-4682) - #15195
Conversation
fish EXPORTS `fish_history`, so an Orca launched from a fish pane keeps the launching worktree's session name in process.env. Every fish pane of the nested app then hit the check-before-set early return and wrote into that one worktree's history file, in every worktree. Drop Orca-minted names (desktop and relay prefixes) wherever the session is injected, and in the history-disabled and daemon spawn paths; a genuine user value still wins.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (2)
Included review availability: Your plan includes up to 10 reviews per rolling hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change adds shared cleanup for inherited Orca-managed 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 833e82dd-2294-43c8-810f-1f043fff9842
📒 Files selected for processing (9)
src/main/daemon/pty-subprocess-env-inheritance.test.tssrc/main/daemon/pty-subprocess.tssrc/main/fish-history-session.tssrc/main/providers/local-pty-provider-spawn-env.test.tssrc/main/providers/local-pty-provider.tssrc/main/terminal-history.test.tssrc/main/terminal-history.tssrc/relay/terminal-history.test.tssrc/relay/terminal-history.ts
Included review availability: Your plan includes up to 10 reviews per rolling hour; 7 remain after this review.
| // Why here too: fish EXPORTS `fish_history`, so the same nesting hands this | ||
| // process the LAUNCHING worktree's session name — and the check-before-set | ||
| // below would honour it, writing every pane's history into that worktree. | ||
| dropInheritedOrcaFishHistory(spawnEnv) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Preserve explicitly supplied Orca-format fish_history values. Session syntax does not establish ownership. The daemon already preserves explicit opts.env.fish_history, but these paths remove the same value after environment merging.
src/main/terminal-history.ts#L192-L195: add explicit-value provenance toinjectHistoryEnvandinjectWslFishHistoryEnv; clean only inherited values.src/main/providers/local-pty-provider.ts#L887-L889: preserveargs.env.fish_historywhen history isolation is disabled, and pass its ownership into the enabled-history path.src/relay/terminal-history.ts#L125-L128: pass caller ownership intoinjectRelayFishHistoryEnvbefore cleanup.src/main/providers/local-pty-provider-spawn-env.test.ts#L191-L211: add anargs.env.fish_history: 'orca_c0ffee'case.src/main/terminal-history.test.ts#L382-L445: test explicit desktop and relay session-format values separately from inherited values.src/relay/terminal-history.test.ts#L48-L68: test an explicitorca_<hash>value after the API carries ownership information.
📍 Affects 3 files
src/main/terminal-history.ts#L192-L195(this comment)src/main/providers/local-pty-provider.ts#L887-L889src/relay/terminal-history.ts#L125-L128
…STA-4682) injectRelayFishHistoryEnv runs only for a fish pane with history isolation on and a worktreeId, so a relay that inherited an Orca-minted fish_history kept it on every other path — scoping those panes to another worktree's history file. The desktop drops it on both branches; scrub it in buildSpawnEnv so relay spawn and revive match. Also record why injectWslFishHistoryEnv keeps its own drop (redundant with both current callers, kept as the function's precondition).
… across Orca instances The relay-specific gate added last round rested on a false premise: main's hasOverlayRestoreEnv already included ORCA_REMOTE_CLI_BIN_DIR, and ssh-pty-spawn-env sets that on every SSH pane whose session has a CLI bridge — so ordinary remote zsh panes were already wrapped. The gate only bit where remoteCliBridgeEnv is null (a host too old to report its platform), where it silently dropped that pane's worktree history. All three transports now share the features.length rule. HISTFILE stays exported, so a newly wrapped pane handed the worktree history path to every child, including a nested Orca whose panes then all hit injectHistoryEnv's check-before-set and appended into the launching worktree's file. Same class as the fish_history fix in #15195: recognise a path Orca minted and drop it before the check, on the desktop, daemon and relay injection paths and both history-disabled branches. Also: run the epilogue from the wrapper .zshrc when zsh is in sh/ksh emulation, since sourcehome() then reads $HOME/.zlogin and the wrapper's .zlogin never runs; track fallback launch-env keys per attempt rather than once from the primary; and guard the cross-file epilogue call so a wrapper dir shared by two builds degrades quietly.
…ive feature channel (#15258) * fix(shell): repair worktree HISTFILE in plain zsh panes via one positive feature channel A plain zsh pane — no startup command, no agent overlay — was never wrapped, so Orca's HISTFILE repair never ran in it. macOS `/etc/zshrc` assigns `HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history` with no check-before-set and runs before any file Orca controls, so per-worktree history was a silent no-op for every ordinary pane on the primary platform. Wrapping those panes needs a way to say which wrapper features a shell should turn on. That channel is one exported variable, ORCA_SHELL_FEATURES, carrying a comma-separated positive allowlist from a closed set (history, markers, ready, identity, overlay). The wrapper .zshenv reads it into a plain, non-exported array and unsets it in its first executable lines, before the user's own .zshenv — so the selection survives .zshenv -> .zprofile -> .zshrc -> .zlogin in this process but physically cannot reach a child. There is no negative or suppression variable anywhere; an absent or inherited value can only ever mean fewer features. ORCA_HISTFILE is consumed and destroyed the same way, which removes the root cause of #11146 instead of patching it. All order-sensitive wrapper work now lives in one `__orca_shell_epilogue` defined in .zshenv and invoked exactly once, from .zshrc for a non-login shell and .zlogin for a login shell, with each feature an independent guard. Selection is a pure function of spawn env and launch intent, so a pane wrapped only for history gets no OSC 133 and is observably identical to the unwrapped pane it used to be. Generation is now fail-closed: wrapper files are written to a temp name and renamed, every required path is verified non-empty, and ZDOTDIR is only set when that holds. Previously a failed write still pointed ZDOTDIR at an empty dir and the user silently lost their entire zsh config. Orca also recognised only its own `*/shell-ready/zsh` dir shape when deciding what the user's ZDOTDIR was, so being launched from any other terminal that had hijacked ZDOTDIR captured that as the user's config dir. Ownership is now established positively — a stamped marker file, or Orca's own dir shape for wrappers written by older builds — and an inherited ZDOTDIR holding no zsh startup file is ignored. No vendor is detected by name. * fix(shell): make the zsh epilogue option-proof and stop history widening relay wrapping Review follow-ups on the feature-channel PR. - `emulate -L zsh` as the epilogue's first statement. It runs after the user's own config, so `setopt no_unset` made the precmd_functions append a fatal error that returned from the whole function (no ready widget, ZDOTDIR left at Orca's wrapper dir), and `setopt ksh_arrays` made the 1-based feature subscript drop whichever feature is listed first. - The `/etc/zshrc` HISTFILE repair is no longer behind the `history` guard: it undoes damage Orca's own ZDOTDIR caused, so it must also run for a shell that re-enters the wrapper after the allowlist was consumed. - The relay keeps its own wrapping gate. Its .zshenv resolves the user's config dir from a ZDOTDIR Orca has already overwritten, so wrapping a remote pane just for `history` cost a relocated-ZDOTDIR user their whole shell config. - A failed primary spawn no longer leaks the primary shell's launch env (wrapper ZDOTDIR + feature channel) into an unwrapped fallback pane. * fix(shell): drop the relay wrapping gate and stop HISTFILE inheriting across Orca instances The relay-specific gate added last round rested on a false premise: main's hasOverlayRestoreEnv already included ORCA_REMOTE_CLI_BIN_DIR, and ssh-pty-spawn-env sets that on every SSH pane whose session has a CLI bridge — so ordinary remote zsh panes were already wrapped. The gate only bit where remoteCliBridgeEnv is null (a host too old to report its platform), where it silently dropped that pane's worktree history. All three transports now share the features.length rule. HISTFILE stays exported, so a newly wrapped pane handed the worktree history path to every child, including a nested Orca whose panes then all hit injectHistoryEnv's check-before-set and appended into the launching worktree's file. Same class as the fish_history fix in #15195: recognise a path Orca minted and drop it before the check, on the desktop, daemon and relay injection paths and both history-disabled branches. Also: run the epilogue from the wrapper .zshrc when zsh is in sh/ksh emulation, since sourcehome() then reads $HOME/.zlogin and the wrapper's .zlogin never runs; track fallback launch-env keys per attempt rather than once from the primary; and guard the cross-file epilogue call so a wrapper dir shared by two builds degrades quietly. * fix(shell): make every wrapper file self-sufficient and retire the deleted marker vars from tests - .zprofile/.zshrc/.zlogin each define __orca_resolve_user_config_dir. They called it on line 2 while only .zshenv defined it, so a wrapper dir written by two concurrently installed builds printed three "command not found" and skipped the user's entire zsh config. New live-shell test covers it. - Retarget every remaining ORCA_SHELL_READY_MARKER/ORCA_SHELL_STARTUP_IDENTITY reference onto ORCA_SHELL_FEATURES, or delete it where the key is now dead. - isOrcaMintedHistFile requires a leading '/', so a relative path of the same shape stays the user's. - Drop an unused no-control-regex disable, and register the two real-zsh suites in the dedicated shell-contracts lane. * fix(shell): stop the zsh wrapper colliding on REPLY and degrade under sh emulation Widening wrapping from overlay/startup panes to every zsh pane turned three latent wrapper defects into user-visible ones. - The config-dir resolver used `REPLY`, zsh's shared scratch global, as its out-parameter. `typeset -r REPLY` in a user config made the wrapper's first executable assignment fatal, `typeset -i REPLY` silently resolved every path to 0; both left HISTFILE inside Orca's wrapper dir. It now writes an Orca-private `_orca_resolved_config_dir`, declared `typeset -g` so the rename introduces no `warn_create_global` noise. A new rule test fails on any generated wrapper file that writes a global outside Orca's namespace. - The daemon dropped an inherited HISTFILE but never an inherited ORCA_HISTFILE, which now both wraps a pane the client scoped nothing for and re-exports another worktree's history path. The relay had the same gap on its isolation-off and revive paths. Both now mirror the desktop. - A user .zshenv or .zprofile ending in `emulate sh` makes zsh ignore ZDOTDIR, so no later wrapper file is read and the epilogue never runs. Nothing can repair HISTFILE from there, so the wrapper now detects the emulation and hands the pane back unwrapped instead of leaving history somewhere invisible. Also `typeset -g __orca_in_command` so the OSC 133 preexec hook prints no warning under `setopt warn_create_global`.
…Orca panes stop merging worktree histories (STA-4682) (stablyai#15195) * fix(terminal-history): drop an inherited Orca fish_history (STA-4682) fish EXPORTS `fish_history`, so an Orca launched from a fish pane keeps the launching worktree's session name in process.env. Every fish pane of the nested app then hit the check-before-set early return and wrote into that one worktree's history file, in every worktree. Drop Orca-minted names (desktop and relay prefixes) wherever the session is injected, and in the history-disabled and daemon spawn paths; a genuine user value still wins. * fix(relay): drop an inherited Orca fish_history on every spawn path (STA-4682) injectRelayFishHistoryEnv runs only for a fish pane with history isolation on and a worktreeId, so a relay that inherited an Orca-minted fish_history kept it on every other path — scoping those panes to another worktree's history file. The desktop drops it on both branches; scrub it in buildSpawnEnv so relay spawn and revive match. Also record why injectWslFishHistoryEnv keeps its own drop (redundant with both current callers, kept as the function's precondition).
…ive feature channel (stablyai#15258) * fix(shell): repair worktree HISTFILE in plain zsh panes via one positive feature channel A plain zsh pane — no startup command, no agent overlay — was never wrapped, so Orca's HISTFILE repair never ran in it. macOS `/etc/zshrc` assigns `HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history` with no check-before-set and runs before any file Orca controls, so per-worktree history was a silent no-op for every ordinary pane on the primary platform. Wrapping those panes needs a way to say which wrapper features a shell should turn on. That channel is one exported variable, ORCA_SHELL_FEATURES, carrying a comma-separated positive allowlist from a closed set (history, markers, ready, identity, overlay). The wrapper .zshenv reads it into a plain, non-exported array and unsets it in its first executable lines, before the user's own .zshenv — so the selection survives .zshenv -> .zprofile -> .zshrc -> .zlogin in this process but physically cannot reach a child. There is no negative or suppression variable anywhere; an absent or inherited value can only ever mean fewer features. ORCA_HISTFILE is consumed and destroyed the same way, which removes the root cause of stablyai#11146 instead of patching it. All order-sensitive wrapper work now lives in one `__orca_shell_epilogue` defined in .zshenv and invoked exactly once, from .zshrc for a non-login shell and .zlogin for a login shell, with each feature an independent guard. Selection is a pure function of spawn env and launch intent, so a pane wrapped only for history gets no OSC 133 and is observably identical to the unwrapped pane it used to be. Generation is now fail-closed: wrapper files are written to a temp name and renamed, every required path is verified non-empty, and ZDOTDIR is only set when that holds. Previously a failed write still pointed ZDOTDIR at an empty dir and the user silently lost their entire zsh config. Orca also recognised only its own `*/shell-ready/zsh` dir shape when deciding what the user's ZDOTDIR was, so being launched from any other terminal that had hijacked ZDOTDIR captured that as the user's config dir. Ownership is now established positively — a stamped marker file, or Orca's own dir shape for wrappers written by older builds — and an inherited ZDOTDIR holding no zsh startup file is ignored. No vendor is detected by name. * fix(shell): make the zsh epilogue option-proof and stop history widening relay wrapping Review follow-ups on the feature-channel PR. - `emulate -L zsh` as the epilogue's first statement. It runs after the user's own config, so `setopt no_unset` made the precmd_functions append a fatal error that returned from the whole function (no ready widget, ZDOTDIR left at Orca's wrapper dir), and `setopt ksh_arrays` made the 1-based feature subscript drop whichever feature is listed first. - The `/etc/zshrc` HISTFILE repair is no longer behind the `history` guard: it undoes damage Orca's own ZDOTDIR caused, so it must also run for a shell that re-enters the wrapper after the allowlist was consumed. - The relay keeps its own wrapping gate. Its .zshenv resolves the user's config dir from a ZDOTDIR Orca has already overwritten, so wrapping a remote pane just for `history` cost a relocated-ZDOTDIR user their whole shell config. - A failed primary spawn no longer leaks the primary shell's launch env (wrapper ZDOTDIR + feature channel) into an unwrapped fallback pane. * fix(shell): drop the relay wrapping gate and stop HISTFILE inheriting across Orca instances The relay-specific gate added last round rested on a false premise: main's hasOverlayRestoreEnv already included ORCA_REMOTE_CLI_BIN_DIR, and ssh-pty-spawn-env sets that on every SSH pane whose session has a CLI bridge — so ordinary remote zsh panes were already wrapped. The gate only bit where remoteCliBridgeEnv is null (a host too old to report its platform), where it silently dropped that pane's worktree history. All three transports now share the features.length rule. HISTFILE stays exported, so a newly wrapped pane handed the worktree history path to every child, including a nested Orca whose panes then all hit injectHistoryEnv's check-before-set and appended into the launching worktree's file. Same class as the fish_history fix in stablyai#15195: recognise a path Orca minted and drop it before the check, on the desktop, daemon and relay injection paths and both history-disabled branches. Also: run the epilogue from the wrapper .zshrc when zsh is in sh/ksh emulation, since sourcehome() then reads $HOME/.zlogin and the wrapper's .zlogin never runs; track fallback launch-env keys per attempt rather than once from the primary; and guard the cross-file epilogue call so a wrapper dir shared by two builds degrades quietly. * fix(shell): make every wrapper file self-sufficient and retire the deleted marker vars from tests - .zprofile/.zshrc/.zlogin each define __orca_resolve_user_config_dir. They called it on line 2 while only .zshenv defined it, so a wrapper dir written by two concurrently installed builds printed three "command not found" and skipped the user's entire zsh config. New live-shell test covers it. - Retarget every remaining ORCA_SHELL_READY_MARKER/ORCA_SHELL_STARTUP_IDENTITY reference onto ORCA_SHELL_FEATURES, or delete it where the key is now dead. - isOrcaMintedHistFile requires a leading '/', so a relative path of the same shape stays the user's. - Drop an unused no-control-regex disable, and register the two real-zsh suites in the dedicated shell-contracts lane. * fix(shell): stop the zsh wrapper colliding on REPLY and degrade under sh emulation Widening wrapping from overlay/startup panes to every zsh pane turned three latent wrapper defects into user-visible ones. - The config-dir resolver used `REPLY`, zsh's shared scratch global, as its out-parameter. `typeset -r REPLY` in a user config made the wrapper's first executable assignment fatal, `typeset -i REPLY` silently resolved every path to 0; both left HISTFILE inside Orca's wrapper dir. It now writes an Orca-private `_orca_resolved_config_dir`, declared `typeset -g` so the rename introduces no `warn_create_global` noise. A new rule test fails on any generated wrapper file that writes a global outside Orca's namespace. - The daemon dropped an inherited HISTFILE but never an inherited ORCA_HISTFILE, which now both wraps a pane the client scoped nothing for and re-exports another worktree's history path. The relay had the same gap on its isolation-off and revive paths. Both now mirror the desktop. - A user .zshenv or .zprofile ending in `emulate sh` makes zsh ignore ZDOTDIR, so no later wrapper file is read and the epilogue never runs. Nothing can repair HISTFILE from there, so the wrapper now detects the emulation and hands the pane back unwrapped instead of leaving history somewhere invisible. Also `typeset -g __orca_in_command` so the OSC 133 preexec hook prints no warning under `setopt warn_create_global`.
…Orca panes stop merging worktree histories (STA-4682) (stablyai#15195) * fix(terminal-history): drop an inherited Orca fish_history (STA-4682) fish EXPORTS `fish_history`, so an Orca launched from a fish pane keeps the launching worktree's session name in process.env. Every fish pane of the nested app then hit the check-before-set early return and wrote into that one worktree's history file, in every worktree. Drop Orca-minted names (desktop and relay prefixes) wherever the session is injected, and in the history-disabled and daemon spawn paths; a genuine user value still wins. * fix(relay): drop an inherited Orca fish_history on every spawn path (STA-4682) injectRelayFishHistoryEnv runs only for a fish pane with history isolation on and a worktreeId, so a relay that inherited an Orca-minted fish_history kept it on every other path — scoping those panes to another worktree's history file. The desktop drops it on both branches; scrub it in buildSpawnEnv so relay spawn and revive match. Also record why injectWslFishHistoryEnv keeps its own drop (redundant with both current callers, kept as the function's precondition).
…ive feature channel (stablyai#15258) * fix(shell): repair worktree HISTFILE in plain zsh panes via one positive feature channel A plain zsh pane — no startup command, no agent overlay — was never wrapped, so Orca's HISTFILE repair never ran in it. macOS `/etc/zshrc` assigns `HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history` with no check-before-set and runs before any file Orca controls, so per-worktree history was a silent no-op for every ordinary pane on the primary platform. Wrapping those panes needs a way to say which wrapper features a shell should turn on. That channel is one exported variable, ORCA_SHELL_FEATURES, carrying a comma-separated positive allowlist from a closed set (history, markers, ready, identity, overlay). The wrapper .zshenv reads it into a plain, non-exported array and unsets it in its first executable lines, before the user's own .zshenv — so the selection survives .zshenv -> .zprofile -> .zshrc -> .zlogin in this process but physically cannot reach a child. There is no negative or suppression variable anywhere; an absent or inherited value can only ever mean fewer features. ORCA_HISTFILE is consumed and destroyed the same way, which removes the root cause of stablyai#11146 instead of patching it. All order-sensitive wrapper work now lives in one `__orca_shell_epilogue` defined in .zshenv and invoked exactly once, from .zshrc for a non-login shell and .zlogin for a login shell, with each feature an independent guard. Selection is a pure function of spawn env and launch intent, so a pane wrapped only for history gets no OSC 133 and is observably identical to the unwrapped pane it used to be. Generation is now fail-closed: wrapper files are written to a temp name and renamed, every required path is verified non-empty, and ZDOTDIR is only set when that holds. Previously a failed write still pointed ZDOTDIR at an empty dir and the user silently lost their entire zsh config. Orca also recognised only its own `*/shell-ready/zsh` dir shape when deciding what the user's ZDOTDIR was, so being launched from any other terminal that had hijacked ZDOTDIR captured that as the user's config dir. Ownership is now established positively — a stamped marker file, or Orca's own dir shape for wrappers written by older builds — and an inherited ZDOTDIR holding no zsh startup file is ignored. No vendor is detected by name. * fix(shell): make the zsh epilogue option-proof and stop history widening relay wrapping Review follow-ups on the feature-channel PR. - `emulate -L zsh` as the epilogue's first statement. It runs after the user's own config, so `setopt no_unset` made the precmd_functions append a fatal error that returned from the whole function (no ready widget, ZDOTDIR left at Orca's wrapper dir), and `setopt ksh_arrays` made the 1-based feature subscript drop whichever feature is listed first. - The `/etc/zshrc` HISTFILE repair is no longer behind the `history` guard: it undoes damage Orca's own ZDOTDIR caused, so it must also run for a shell that re-enters the wrapper after the allowlist was consumed. - The relay keeps its own wrapping gate. Its .zshenv resolves the user's config dir from a ZDOTDIR Orca has already overwritten, so wrapping a remote pane just for `history` cost a relocated-ZDOTDIR user their whole shell config. - A failed primary spawn no longer leaks the primary shell's launch env (wrapper ZDOTDIR + feature channel) into an unwrapped fallback pane. * fix(shell): drop the relay wrapping gate and stop HISTFILE inheriting across Orca instances The relay-specific gate added last round rested on a false premise: main's hasOverlayRestoreEnv already included ORCA_REMOTE_CLI_BIN_DIR, and ssh-pty-spawn-env sets that on every SSH pane whose session has a CLI bridge — so ordinary remote zsh panes were already wrapped. The gate only bit where remoteCliBridgeEnv is null (a host too old to report its platform), where it silently dropped that pane's worktree history. All three transports now share the features.length rule. HISTFILE stays exported, so a newly wrapped pane handed the worktree history path to every child, including a nested Orca whose panes then all hit injectHistoryEnv's check-before-set and appended into the launching worktree's file. Same class as the fish_history fix in stablyai#15195: recognise a path Orca minted and drop it before the check, on the desktop, daemon and relay injection paths and both history-disabled branches. Also: run the epilogue from the wrapper .zshrc when zsh is in sh/ksh emulation, since sourcehome() then reads $HOME/.zlogin and the wrapper's .zlogin never runs; track fallback launch-env keys per attempt rather than once from the primary; and guard the cross-file epilogue call so a wrapper dir shared by two builds degrades quietly. * fix(shell): make every wrapper file self-sufficient and retire the deleted marker vars from tests - .zprofile/.zshrc/.zlogin each define __orca_resolve_user_config_dir. They called it on line 2 while only .zshenv defined it, so a wrapper dir written by two concurrently installed builds printed three "command not found" and skipped the user's entire zsh config. New live-shell test covers it. - Retarget every remaining ORCA_SHELL_READY_MARKER/ORCA_SHELL_STARTUP_IDENTITY reference onto ORCA_SHELL_FEATURES, or delete it where the key is now dead. - isOrcaMintedHistFile requires a leading '/', so a relative path of the same shape stays the user's. - Drop an unused no-control-regex disable, and register the two real-zsh suites in the dedicated shell-contracts lane. * fix(shell): stop the zsh wrapper colliding on REPLY and degrade under sh emulation Widening wrapping from overlay/startup panes to every zsh pane turned three latent wrapper defects into user-visible ones. - The config-dir resolver used `REPLY`, zsh's shared scratch global, as its out-parameter. `typeset -r REPLY` in a user config made the wrapper's first executable assignment fatal, `typeset -i REPLY` silently resolved every path to 0; both left HISTFILE inside Orca's wrapper dir. It now writes an Orca-private `_orca_resolved_config_dir`, declared `typeset -g` so the rename introduces no `warn_create_global` noise. A new rule test fails on any generated wrapper file that writes a global outside Orca's namespace. - The daemon dropped an inherited HISTFILE but never an inherited ORCA_HISTFILE, which now both wraps a pane the client scoped nothing for and re-exports another worktree's history path. The relay had the same gap on its isolation-off and revive paths. Both now mirror the desktop. - A user .zshenv or .zprofile ending in `emulate sh` makes zsh ignore ZDOTDIR, so no later wrapper file is read and the epilogue never runs. Nothing can repair HISTFILE from there, so the wrapper now detects the emulation and hands the pane back unwrapped instead of leaving history somewhere invisible. Also `typeset -g __orca_in_command` so the OSC 133 preexec hook prints no warning under `setopt warn_create_global`.
…Orca panes stop merging worktree histories (STA-4682) (stablyai#15195) * fix(terminal-history): drop an inherited Orca fish_history (STA-4682) fish EXPORTS `fish_history`, so an Orca launched from a fish pane keeps the launching worktree's session name in process.env. Every fish pane of the nested app then hit the check-before-set early return and wrote into that one worktree's history file, in every worktree. Drop Orca-minted names (desktop and relay prefixes) wherever the session is injected, and in the history-disabled and daemon spawn paths; a genuine user value still wins. * fix(relay): drop an inherited Orca fish_history on every spawn path (STA-4682) injectRelayFishHistoryEnv runs only for a fish pane with history isolation on and a worktreeId, so a relay that inherited an Orca-minted fish_history kept it on every other path — scoping those panes to another worktree's history file. The desktop drops it on both branches; scrub it in buildSpawnEnv so relay spawn and revive match. Also record why injectWslFishHistoryEnv keeps its own drop (redundant with both current callers, kept as the function's precondition).
…ive feature channel (stablyai#15258) * fix(shell): repair worktree HISTFILE in plain zsh panes via one positive feature channel A plain zsh pane — no startup command, no agent overlay — was never wrapped, so Orca's HISTFILE repair never ran in it. macOS `/etc/zshrc` assigns `HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history` with no check-before-set and runs before any file Orca controls, so per-worktree history was a silent no-op for every ordinary pane on the primary platform. Wrapping those panes needs a way to say which wrapper features a shell should turn on. That channel is one exported variable, ORCA_SHELL_FEATURES, carrying a comma-separated positive allowlist from a closed set (history, markers, ready, identity, overlay). The wrapper .zshenv reads it into a plain, non-exported array and unsets it in its first executable lines, before the user's own .zshenv — so the selection survives .zshenv -> .zprofile -> .zshrc -> .zlogin in this process but physically cannot reach a child. There is no negative or suppression variable anywhere; an absent or inherited value can only ever mean fewer features. ORCA_HISTFILE is consumed and destroyed the same way, which removes the root cause of stablyai#11146 instead of patching it. All order-sensitive wrapper work now lives in one `__orca_shell_epilogue` defined in .zshenv and invoked exactly once, from .zshrc for a non-login shell and .zlogin for a login shell, with each feature an independent guard. Selection is a pure function of spawn env and launch intent, so a pane wrapped only for history gets no OSC 133 and is observably identical to the unwrapped pane it used to be. Generation is now fail-closed: wrapper files are written to a temp name and renamed, every required path is verified non-empty, and ZDOTDIR is only set when that holds. Previously a failed write still pointed ZDOTDIR at an empty dir and the user silently lost their entire zsh config. Orca also recognised only its own `*/shell-ready/zsh` dir shape when deciding what the user's ZDOTDIR was, so being launched from any other terminal that had hijacked ZDOTDIR captured that as the user's config dir. Ownership is now established positively — a stamped marker file, or Orca's own dir shape for wrappers written by older builds — and an inherited ZDOTDIR holding no zsh startup file is ignored. No vendor is detected by name. * fix(shell): make the zsh epilogue option-proof and stop history widening relay wrapping Review follow-ups on the feature-channel PR. - `emulate -L zsh` as the epilogue's first statement. It runs after the user's own config, so `setopt no_unset` made the precmd_functions append a fatal error that returned from the whole function (no ready widget, ZDOTDIR left at Orca's wrapper dir), and `setopt ksh_arrays` made the 1-based feature subscript drop whichever feature is listed first. - The `/etc/zshrc` HISTFILE repair is no longer behind the `history` guard: it undoes damage Orca's own ZDOTDIR caused, so it must also run for a shell that re-enters the wrapper after the allowlist was consumed. - The relay keeps its own wrapping gate. Its .zshenv resolves the user's config dir from a ZDOTDIR Orca has already overwritten, so wrapping a remote pane just for `history` cost a relocated-ZDOTDIR user their whole shell config. - A failed primary spawn no longer leaks the primary shell's launch env (wrapper ZDOTDIR + feature channel) into an unwrapped fallback pane. * fix(shell): drop the relay wrapping gate and stop HISTFILE inheriting across Orca instances The relay-specific gate added last round rested on a false premise: main's hasOverlayRestoreEnv already included ORCA_REMOTE_CLI_BIN_DIR, and ssh-pty-spawn-env sets that on every SSH pane whose session has a CLI bridge — so ordinary remote zsh panes were already wrapped. The gate only bit where remoteCliBridgeEnv is null (a host too old to report its platform), where it silently dropped that pane's worktree history. All three transports now share the features.length rule. HISTFILE stays exported, so a newly wrapped pane handed the worktree history path to every child, including a nested Orca whose panes then all hit injectHistoryEnv's check-before-set and appended into the launching worktree's file. Same class as the fish_history fix in stablyai#15195: recognise a path Orca minted and drop it before the check, on the desktop, daemon and relay injection paths and both history-disabled branches. Also: run the epilogue from the wrapper .zshrc when zsh is in sh/ksh emulation, since sourcehome() then reads $HOME/.zlogin and the wrapper's .zlogin never runs; track fallback launch-env keys per attempt rather than once from the primary; and guard the cross-file epilogue call so a wrapper dir shared by two builds degrades quietly. * fix(shell): make every wrapper file self-sufficient and retire the deleted marker vars from tests - .zprofile/.zshrc/.zlogin each define __orca_resolve_user_config_dir. They called it on line 2 while only .zshenv defined it, so a wrapper dir written by two concurrently installed builds printed three "command not found" and skipped the user's entire zsh config. New live-shell test covers it. - Retarget every remaining ORCA_SHELL_READY_MARKER/ORCA_SHELL_STARTUP_IDENTITY reference onto ORCA_SHELL_FEATURES, or delete it where the key is now dead. - isOrcaMintedHistFile requires a leading '/', so a relative path of the same shape stays the user's. - Drop an unused no-control-regex disable, and register the two real-zsh suites in the dedicated shell-contracts lane. * fix(shell): stop the zsh wrapper colliding on REPLY and degrade under sh emulation Widening wrapping from overlay/startup panes to every zsh pane turned three latent wrapper defects into user-visible ones. - The config-dir resolver used `REPLY`, zsh's shared scratch global, as its out-parameter. `typeset -r REPLY` in a user config made the wrapper's first executable assignment fatal, `typeset -i REPLY` silently resolved every path to 0; both left HISTFILE inside Orca's wrapper dir. It now writes an Orca-private `_orca_resolved_config_dir`, declared `typeset -g` so the rename introduces no `warn_create_global` noise. A new rule test fails on any generated wrapper file that writes a global outside Orca's namespace. - The daemon dropped an inherited HISTFILE but never an inherited ORCA_HISTFILE, which now both wraps a pane the client scoped nothing for and re-exports another worktree's history path. The relay had the same gap on its isolation-off and revive paths. Both now mirror the desktop. - A user .zshenv or .zprofile ending in `emulate sh` makes zsh ignore ZDOTDIR, so no later wrapper file is read and the epilogue never runs. Nothing can repair HISTFILE from there, so the wrapper now detects the emulation and hands the pane back unwrapped instead of leaving history somewhere invisible. Also `typeset -g __orca_in_command` so the OSC 133 preexec hook prints no warning under `setopt warn_create_global`.
ELI5
fish is unusual: it broadcasts (
exports) itsfish_historyvariable to every program it starts. So if you launch Orca from a fish terminal inside another Orca, the new Orca inheritsfish_history=orca_<hash-of-that-worktree>— and every fish pane in the new app, in any worktree, wrote its commands into that one worktree's history file. This PR throws away an inherited history name that Orca itself minted, while still respecting afish_historyyou set yourself.What Changed
dropInheritedOrcaFishHistory(env)insrc/main/fish-history-session.ts, built on the existingisSafeFishHistorySessionpredicate (matches both theorca_andorca_relay_prefixes — a relay pane should drop a desktop-minted inherited name and vice versa; neither owns the other's worktree ids).injectHistoryEnv(src/main/terminal-history.ts), right beside the existing unconditionaldelete spawnEnv.ORCA_HISTFILE, so the fish check-before-set early return can no longer honour an inherited name.injectWslFishHistoryEnv(src/main/terminal-history.ts).injectRelayFishHistoryEnv(src/relay/terminal-history.ts).PtyHandler.buildSpawnEnv(src/relay/pty-handler.ts), unconditionally, alongside the relay's other inherited-env scrubbers (stripInheritedBuildModeEnv,stripLegacyTerminalShimEnv).LocalPtyProvider.spawn(src/main/providers/local-pty-provider.ts), which previously cleared onlyORCA_HISTFILE.createPtySubprocess(src/main/daemon/pty-subprocess.ts), guarded byopts.env?.fish_history === undefined— same shape as the neighbouringremoveUnspecifiedPaneIdentityEnv, so the session the adapter'sinjectHistoryEnvjust minted (and any explicit caller value) still wins over the daemon's own inherited env.No wire-format, protocol, or persisted-state change; this is spawn-env only.
Why
src/main/terminal-history.tsalready deletedORCA_HISTFILEunconditionally for exactly this reason (#11146), but did the opposite for fish:if (shell === 'fish' ? spawnEnv.fish_history : spawnEnv.HISTFILE) { return result }, over a spawn env seeded fromstripInheritedBuildModeEnv(process.env). Measured on fish 4.7.1:So the name really is exported, and a nested Orca kept it. Result: cross-worktree history merge — the leak the feature exists to prevent.
Dropping only names that match Orca's own minted shape keeps the check-before-set contract for a user's genuine
fish_history(the same reasoning the orphan sweep already documents infish-history-session.ts). A dropped name is re-minted from this worktree's id, which is deterministic, so nothing changes for a normal (non-nested) launch.Relay symmetry (review follow-up)
injectRelayFishHistoryEnvis called fromsrc/relay/pty-handler.ts(spawn:1558, revive:2163) only whenhistoryIsolationEnabled && worktreeId && basename(shell).toLowerCase().startsWith('fish'). Putting the drop only inside it left a relay pane that inherits an Orca-shapedfish_historyon any other path — isolation off, noworktreeId, or a non-fish shell — still scoped to someone else's worktree, while the desktop dropped it on both branches. The drop now also runs unconditionally inbuildSpawnEnv, the single choke point both spawn and revive pass through and where the relay's other inherited-env scrubbing already lives.Two same-shape redundancies were kept deliberately, each with a comment saying so:
injectRelayFishHistoryEnv's drop is that function's own precondition for its check-before-set, not the caller's. It is a no-op in production now thatbuildSpawnEnvscrubs first.injectWslFishHistoryEnv's drop is likewise a no-op today: both callers (local-pty-provider.ts:879,daemon-pty-adapter.ts:498) runinjectHistoryEnvon the same env object immediately before, and that already drops. Kept because nothing but call ordering enforces it.Linked Issue
STA-4682 (claim 5). Follow-up to #15018.
Fixes #
Visual Proof
N/A— shell environment only; no rendered UI or interaction changes.Testing
macOS (fish 4.7.1 installed locally) — targeted vitest files plus
pnpm typecheck,npx oxlint, andnpx oxfmton the changed files. WSL/relay/daemon paths are covered by unit tests, not by a live run.Vacuity check — each source change reverted in turn with the new tests kept:
terminal-history› replaces a desktop fish_history inherited from a parent Orcaterminal-history› replaces a relay fish_history inherited from a parent Orcaterminal-history› drops an inherited fish_history even when the shell is not fishinjectWslFishHistoryEnv› replaces an inherited Orca fish_history with this worktree sessionPtyHandler› isolation off › a relay-minted session inherited from the relay process envPtyHandler› isolation off › a desktop-minted session inherited from the relay process envPtyHandler› isolation off › drops a desktop-minted session handed over in the client envLocalPtyProvider› history isolation off: an inherited Orca sessioncreatePtySubprocess› drops an inherited Orca fish_historypreserves a caller-supplied fish_history×3,a user value,keeps a user value,keeps the session this spawn injected,keeps a caller-supplied value)The preservation half of each pair passing in both states is the point: the fix must not turn into "always overwrite
fish_history".fish_history, the premise of the bug)AI Disclosure
Claude Opus.
Review
Worth a close look at the daemon guard in
pty-subprocess.ts: it deliberately trustsopts.env.fish_history(which is wheredaemon-pty-adapter'sinjectHistoryEnvwrites the isolated session) and only scrubs the daemon's own inheritedprocess.envvalue.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Deliberately out of scope — STA-4682's other claims are not in this PR:
buildSpawnEnv= relayprocess.env+ renderer env + augmenters, and no writer putsHOME/XDG_DATA_HOMEinto a pane spawn env, so the spawn env's fish data dir cannot differ from the relay process's.nounsetand$fish_pid): a documented, measured tradeoff atsrc/shared/tui-agent-startup-shell.ts:323-325; the naive alternative was re-tested and still fails (fish 4unsetprints an uncaught unknown-command diagnostic).Same class, different variable — noticed while here and not fixed, each a follow-up rather than claim-5 scope:
HISTFILEinherited from a parent Orca pane.injectHistoryEnv's non-fish check-before-set returns early whenspawnEnv.HISTFILEis already set (src/main/terminal-history.ts:213), leavingORCA_HISTFILEunset and the pane pointed at the inherited path.HISTFILEreaches a pane through the spawn env, so zsh and bash mark it exported and a nested Orca inherits the launching worktree's history file — the same shape as this fish bug, and nothing on the spawn path strips it. A separate PR on this branch stack covers the zsh/HISTFILEside, so fixing it here would collide.ORCA_HISTFILE. The daemon has no equivalent scrub for an inheritedORCA_HISTFILEwhen history isolation is off or the session is a reattach.Cross-platform / SSH / mobile / perf: the change is a regex test over one env var at spawn time (no I/O, no new syscalls). WSL and relay paths get the same treatment as native. Folder workspaces and git worktrees are indistinguishable here — everything keys off
worktreeIdas before. Backwards compatible: session names are a pure function of the worktree id, so a re-minted name is byte-identical to the one a non-nested launch already used.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred) — ran oxlint + oxfmt +pnpm typecheck+ targeted vitest locally; full suite left to CI