expected structural change for 5.x

This commit is contained in:
2026-10-13 18:17:34 +02:00
parent ea1a8a7a7e
commit 069457b3e9
+49
View File
@@ -104,6 +104,55 @@ version-bump.
---
## Structure
- [ ] **Helpers must not stay in the environment (do first)** — internal
functions (`_` prefix, `helpers/`) are not part of the user interface, yet 16
of them are `export -f`, so they leak into the environment of every child
process. By design they should only exist where they are needed:
- stop exporting helpers by default; unset the ones only needed at load time
(as `profile.sh` already does for `parse_conf`, `load_alias`, ...);
- a helper stays exported only when an exported public function calls it and
must work in a child bash (`bash -c`, `find -exec`, `xargs`), which has to
be checked and documented one by one (otherwise that public function is no
longer exported either, or waits for the stub mechanism below);
- add a check (shellcheck-like script or `bats` test) listing exported
functions starting with `_`, so none comes back unnoticed.
This is the first step: it does not depend on the lazy loading below, which
builds on it. **[medium]**
- [ ] **Load code on demand (stubs, lazy loading, optional unloading)** —
today every module is sourced at startup (about 63 ms, 109 functions) and 80
functions are exported (about 250 KB of environment inherited by every
external command, which makes each one about 25 % slower and eats `ARG_MAX`).
Replace this by:
- *Stubs*: each lazy command is a tiny generated function
(`rain() { _lazy rain "$@"; }`), the only thing exported. The stubs come
from a command-to-module index, built at upgrade time from a
`# provides: ...` header in each module (so `help`, `type` and completion
still see every command).
- *Lazy loading* (`_try_load`): the first call sources the module and its
`helpers/<module>.sh` (and the modules it depends on, declared in the
header), then runs the real function (`_exec`). The code stays loaded
afterwards. Helpers and implementations are no longer exported, which also
removes them from the environment of child shells.
- *Optional unloading* (`_free`, e.g. `PROFILE_UNLOAD=1`): after the call,
put the stub back instead of unsetting the function. Must be idempotent
(Ctrl-C or `return` skip the cleanup), must not free code still referenced
by a trap (`_rain_stop`, WINCH handler) or used by a running caller
(reference count), and costs about 4 ms per call, so it is not the default.
- *Always resident*: `disp`, `conf`, `debug`, `prompt` (and the `DEBUG` timer
hook), `lang` shortcuts and `help`. With unloading, state kept across calls
(`pwd` pool, current theme, `settrace`) must live in variables that are not
freed. Lazy candidates: `compress`,
`filefct`, `fun`, `git`, `info`, `net`, `packages`, `processes`, `pwd`,
`rain`, `ssh`, `updates`.
- *To solve*: `load_conf`/`CONF_*` must stay available at call time; child
shells need `MYPATH` and the loader exported to resolve stubs; tracebacks
show the loader frames. Do it after the test suite exists, as regressions
would be silent. **[hard]**
---
## Infrastructure
- [ ] **Test suite** — add a `test/` directory with `bats` (Bash Automated