Files
tai/research/shell-multiplexer-auto-attach/fishshell-docs-3-4-index-initialization-files.md
T
2026-10-10 09:25:33 +02:00

3.7 KiB

fish docs index: initialization files (config.fish is always sourced)

Metadata

Field Value
Institute Molodetz Institute — Research & Retrieval Division
Subject shell-multiplexer-auto-attach
Document type Source Dossier
Tier 1
Source title fish documentation index (3.4)
Source URL https://fishshell.com/docs/3.4/index.html
Source publisher / author fish-shell project (fishshell.com)
Source type vendor docs
Source date (published) Unknown — not stated
Date researched 2026-10-10
Researched by tai (agent), model: Claude Sonnet 5, on behalf of retoor
Research method web search synthesis
Confidence High
Status Active

Summary

fish's official docs state plainly that its startup files are read "on the startup of every shell, not just interactive ones" — unlike bash's .bashrc (interactive-only, non-login) or .profile/.bash_profile (login-only). ~/.config/fish/config.fish (respecting $XDG_CONFIG_HOME) is read on every fish invocation, interactive or not, login or not, and is explicitly the right place to put code that should run unconditionally — with status is-interactive/status is-login as the documented way to scope behavior within it. Snippets in ~/.config/fish/conf.d/ run before config.fish, which runs last so it can override them.

Key findings

  • This is the single most consequential difference from bash/zsh for this project: .bashrc and .zshrc are themselves gated to interactive shells by the shell's own startup logic (roughly), so the bash/zsh plexer block's case "$-" in *i*) guard is a belt-and-suspenders check on top of a file that mostly only gets read in the right context anyway. config.fish has no such built-in gating — it runs unconditionally, so the status is-interactive guard inside FISH_PLEXER_BLOCK is doing all of the interactivity-scoping work, not just reinforcing it.
  • The fish FAQ (per the search tool's synthesis) explicitly calls out this exact contrast with .bashrc/.profile, confirming it's a documented, intentional design difference in fish, not an edge case this research happened to stumble on.
  • ~/.config/fish/config.fish's location follows $XDG_CONFIG_HOME — directly informing shell_rc_path() in tai.py, which checks os.environ.get("XDG_CONFIG_HOME") before falling back to ~/.config.

Pros / strengths

  • Official documentation stating the behavior plainly and explicitly contrasting it with bash/zsh conventions, removing any ambiguity about whether the interactivity guard is optional.

Cons / limitations / caveats

  • A third-party pyenv wiki page (github-pyenv-wiki-unix-shell-initialization.md, also Tier 1) lists a fuller startup-file ordering (system/user conf.d, vendor_conf.d, /etc/fish/config.fish, then ~/.config/fish/config.fish) that this search could not independently confirm against the official docs; treated as secondary/unconfirmed per that dossier's own caveat, not relied on for anything load-bearing here.

Relevance to tai

This is the finding that made the status is-interactive guard non-optional in FISH_PLEXER_BLOCK: without it, installing the plexer hook into config.fish would attempt to exec tai.py --plexer-wrap on every fish invocation, including scripts, fish -c, and tooling that shells out to fish non-interactively — exactly the failure mode the user explicitly wanted to avoid. Confirmed end-to-end by the FishNonInteractiveSkipTest integration test in tests/bashrc.py, which runs a plain fish -c 'echo marker' (no explicit sourcing needed, since config.fish always runs) and asserts no plexer socket gets created.

Changelog

Date Change
2026-10-10 Initial dossier created.