Opened after the zsh module's rollout (to-be 38 WP5) was stopped: every machine carries the same predecessor-written startup file, the module's block would duplicate it and drop lines, nothing installs the prompt, and execute never reads .zshrc. Weighs how modules contribute environment and shell code, where the operator's own lines go, ordering, and what a contribution is addressed to.
6.4 KiB
01 — What the shell file holds today
Measured 2026-10-04 on the four machines of one installation: two servers and two workstations. All four have the account's login shell set to zsh, zsh installed from the distribution, and a predecessor-written startup file. The predecessor is retired, so nothing manages these files any more.
The startup file is the same everywhere
The account's ~/.zshrc is byte-identical on all four machines: 102 lines, one checksum.
~/.zshrc.local, which the last line of ~/.zshrc sources, comes in two variants: one shared by
both servers, and one shared by both workstations. So the "per-machine" part is really a
per-kind-of-machine part.
The predecessor produced these from one module with two flavors: a prompt flavor and an autocomplete flavor, each of which swapped in a different local file. Its install hook also:
- cloned the prompt theme and three plugins from their upstream repositories into
~/.zsh/; - installed fonts;
- changed the login shell.
On the workstations the theme and plugins are still on disk, left over and now owned by nothing. The servers have none of them.
What the 102 lines are
Sorted by who should own each line once the machine is modules:
| Lines today | What they are | Natural owner |
|---|---|---|
EDITOR, VISUAL, XDG_CONFIG_HOME, PATH gaining ~/.local/bin and two script directories |
the account's environment | the shell's default, or the environment itself |
PATH gaining a toolchain's directory |
environment, for one tool | the toolchain's module |
| a version manager's directory variable plus sourcing its loader | environment and shell code | the version manager's module |
| two variables naming the operator's own script library | environment, the operator's own | the operator |
| a variable that turns off an agent's terminal-title handling | environment, for one tool | the agent's module |
the terminal title hook, keybindings, dircolors, the ls/grep aliases, ll/la/l, a container-run alias, two disk-usage functions, two port aliases |
interactive shell behaviour | the shell's default |
| the prompt's instant-prompt cache, the theme, the prompt's own configuration file | shell code, order-sensitive (instant prompt first) | the prompt module |
| autosuggestions, syntax highlighting (and, unloaded, an autocomplete plugin on disk) | shell code, order-sensitive (syntax highlighting last) | a plugin module, or the prompt module |
sourcing ~/.zshrc.local |
the operator's hook | the operator |
The workstation variant of the local file adds:
- more environment: a desktop toolkit theme, a file manager's plugin list,
BROWSER,VISUALoverridden to a graphical editor, a language toolchain's binary directory onPATH; - two pieces of shell code: one that pads the prompt to the bottom of the terminal under a display, and one that sources a function file another module places;
- a hook sourcing a further per-node file.
The server variant holds only that last module-placed source line.
Count: a workstation runs 65 non-comment lines from the two files (53 shared, 12 local); a server runs 54. Of a workstation's 65:
- about a quarter (15) are environment;
- about half are interactive defaults no other module cares about;
- the remaining quarter is other modules' code and hooks (a prompt, plugins, a version manager, an agent's functions), loaded from the shell file only because there was nowhere else to put it.
The shell module as written
The zsh module of to-be 38 WP5 (catalogue change, unmerged):
- writes one block, appended at the end of
~/.zshrc, holding a subset of the shared file:- its environment lines, minus the toolchain directory, the version manager and the agent variable;
- the title hook, keybindings and the most common aliases, minus the port aliases;
- guarded
sourcelines for the theme and two plugins if present; - the source of
~/.zshrc.local.
- assigned to any of the four machines, appends that block after the identical lines already there, so
every line in it runs twice,
~/.zshrc.localincluded. - on the servers, the guarded prompt lines find nothing; nothing installs the theme anywhere.
Which startup file reaches what
zsh's startup order, and what each path through it reads:
| started as | reads |
|---|---|
| interactive login (a console, ssh with a terminal) | .zshenv, .zprofile, .zshrc, .zlogin |
| interactive non-login (a new terminal window) | .zshenv, .zshrc |
non-interactive login: zsh -lc …, what the execute verb runs |
.zshenv, .zprofile, .zlogin, not .zshrc |
non-interactive: a script, ssh host command |
.zshenv only |
So an environment written into .zshrc reaches neither execute nor a script. The distribution's
system-wide login profile, which zsh's system zprofile sources, only ever appends to PATH when an
entry is missing. An entry the account's .zshenv puts first therefore survives a login.
A graphical session's programs (a launcher, a bar, a window manager's key bindings) are started from the
display manager and the service manager, not from a shell, and read none of these files. The service
manager's own place for the account's environment is ~/.config/environment.d/. Today it holds nothing
on any of the four machines, so a program launched from the window manager does not see PATH entries
that a terminal does.
What the mesh already has for "many modules, one file"
Measured over the catalogue's 69 module definitions:
| mechanism | used by | shape |
|---|---|---|
contributes / receives |
28 contribute, 15 receive | A consumer contributes facts keyed by a requirement. The provider receives all of them as one file in the mesh's own format, and renders them itself. "The controller does not know what a reverse proxy is." |
listens / filtering |
40 declare listens, 1 composes | The controller derives the whole firewall rule set from every module's ports and writes it where the holder asks. |
jails / jailing |
3 declare, 1 composes | Each module supplies its jail in the tool's own format. The controller assembles them, sorted, into the one file the holder names. |
into: block on a file |
2 | One module's marked region inside a file something else owns. Text outside the region is kept byte for byte. Placement is at the end, or at the start. |
None of these is a contribution of shell code or of environment today.