fix: guard and document the external tools PR #168 flagged as unguarded #170

Merged
rootiest merged 1 commits from docs/guard-external-deps into main 2026-09-23 03:00:02 +00:00
Owner

Summary

PR #168's Notes section named several functions with a real external-tool dependency that no type -q/command -q/command -v/which guard covers anywhere in the tree, deliberately left out of DEPENDENCIES to avoid breaking test_dependencies_resolve. This adds the missing guard to each, then declares the dependency now that it resolves.

Function(s) Dependency added
bkg, detach nohup
gitui gitui (self-shadow — guarded with type -q -f to skip the function itself)
play-media mpv, vlc — see the bug below, this one was already guarded
steam-dl systemd-inhibit, steam
wake-lock systemd-inhibit
split, spwin, tab wezterm, konsole (kitty was already declared)

A real bug in the checker, not just missing guards

play-media.fish already guards its mpv/vlc fallback with type -q -f $p inside for p in mpv vlc — it just wasn't recognized as a guard. docs/verify-manual.py's guard-detection regex only matched type -q <name> with the name immediately following -q, so the -f flag (which excludes functions from the match — needed anywhere a wrapper shadows a binary of its own name, e.g. gitui) broke both the direct match and the loop-variable indirection. That's why the previous PR's own tooling reported it as unguarded when it wasn't. Broadened both patterns in test_dependencies_resolve to skip over any flags between -q and the name/variable.

A related latent bug: $TERM checks don't prove the binary exists

split/spwin/tab dispatch on $TERM/$TERM_PROGRAM/$KONSOLE_VERSION to pick which terminal-specific binary to call, per this repo's C4 convention — but those env vars only prove the terminal type, not that its CLI binary is on $PATH. They propagate over ssh, so sshing out from Kitty/WezTerm inherits the var on a remote host that never installed the binary, and the function would previously hand that straight to kitty @ launch/wezterm cli .../konsole with no existence check, surfacing a raw "command not found" instead of a clean error. clone/clonet have the identical gap: clone-in-kitty is a function Kitty's own shell integration injects into the local shell, not present on a remote shell that only inherited $TERM=xterm-kitty. All five functions now check the actual thing they're about to call, in addition to the $TERM-based dispatch that picks which one to check.

Found by the same audit, same pattern, not in PR #168's list

fast-cli (fast), lock (loginctl), ports (lsof), screensleep (busctl) had the identical shape — a real external-tool dependency, called with no existence guard and no DEPENDENCIES entry. Fixed alongside the named ones since it's the same oversight, not a separate task.

Deliberately left alone

spark (awk), sudo-toggle (sudo/stat/truncate/tee) call only baseline POSIX/coreutils tools already assumed present everywhere else in the tree unguarded — adding guards there would be inconsistent with how the rest of the manual treats those tools. swapstat's zramctl/swapon already degrade gracefully via an output-emptiness check rather than an existence check; that's a different, already-adequate pattern, not an oversight.

Verification

  • python3 docs/build-manual.py --concat -o docs/fish-config.md — regenerated.
  • python3 docs/verify-manual.py — 84/84 passed, test_dependencies_resolve included (now resolves every new entry above).
  • fish tests/run-tests.fish — 757/757 assertions passed, exit 0, syntax/indent lint 253/253, shadow-classification lint 196/196.
  • fish_indent byte-check on every changed .fish file — clean.
## Summary PR #168's Notes section named several functions with a real external-tool dependency that no `type -q`/`command -q`/`command -v`/`which` guard covers anywhere in the tree, deliberately left out of `DEPENDENCIES` to avoid breaking `test_dependencies_resolve`. This adds the missing guard to each, then declares the dependency now that it resolves. | Function(s) | Dependency added | |---|---| | `bkg`, `detach` | `nohup` | | `gitui` | `gitui` (self-shadow — guarded with `type -q -f` to skip the function itself) | | `play-media` | `mpv`, `vlc` — see the bug below, this one was already guarded | | `steam-dl` | `systemd-inhibit`, `steam` | | `wake-lock` | `systemd-inhibit` | | `split`, `spwin`, `tab` | `wezterm`, `konsole` (`kitty` was already declared) | ## A real bug in the checker, not just missing guards `play-media.fish` already guards its `mpv`/`vlc` fallback with `type -q -f $p` inside `for p in mpv vlc` — it just wasn't recognized as a guard. `docs/verify-manual.py`'s guard-detection regex only matched `type -q <name>` with the name immediately following `-q`, so the `-f` flag (which excludes functions from the match — needed anywhere a wrapper shadows a binary of its own name, e.g. `gitui`) broke both the direct match and the loop-variable indirection. That's why the previous PR's own tooling reported it as unguarded when it wasn't. Broadened both patterns in `test_dependencies_resolve` to skip over any flags between `-q` and the name/variable. ## A related latent bug: `$TERM` checks don't prove the binary exists `split`/`spwin`/`tab` dispatch on `$TERM`/`$TERM_PROGRAM`/`$KONSOLE_VERSION` to pick which terminal-specific binary to call, per this repo's C4 convention — but those env vars only prove the *terminal type*, not that its CLI binary is on `$PATH`. They propagate over `ssh`, so sshing out from Kitty/WezTerm inherits the var on a remote host that never installed the binary, and the function would previously hand that straight to `kitty @ launch`/`wezterm cli ...`/`konsole` with no existence check, surfacing a raw "command not found" instead of a clean error. `clone`/`clonet` have the identical gap: `clone-in-kitty` is a function Kitty's own shell integration injects into the *local* shell, not present on a remote shell that only inherited `$TERM=xterm-kitty`. All five functions now check the actual thing they're about to call, in addition to the `$TERM`-based dispatch that picks *which* one to check. ## Found by the same audit, same pattern, not in PR #168's list `fast-cli` (`fast`), `lock` (`loginctl`), `ports` (`lsof`), `screensleep` (`busctl`) had the identical shape — a real external-tool dependency, called with no existence guard and no `DEPENDENCIES` entry. Fixed alongside the named ones since it's the same oversight, not a separate task. ## Deliberately left alone `spark` (`awk`), `sudo-toggle` (`sudo`/`stat`/`truncate`/`tee`) call only baseline POSIX/coreutils tools already assumed present everywhere else in the tree unguarded — adding guards there would be inconsistent with how the rest of the manual treats those tools. `swapstat`'s `zramctl`/`swapon` already degrade gracefully via an output-emptiness check rather than an existence check; that's a different, already-adequate pattern, not an oversight. ## Verification - [x] `python3 docs/build-manual.py --concat -o docs/fish-config.md` — regenerated. - [x] `python3 docs/verify-manual.py` — 84/84 passed, `test_dependencies_resolve` included (now resolves every new entry above). - [x] `fish tests/run-tests.fish` — 757/757 assertions passed, exit 0, syntax/indent lint 253/253, shadow-classification lint 196/196. - [x] `fish_indent` byte-check on every changed `.fish` file — clean.
rootiest added 1 commit 2026-09-23 02:41:06 +00:00
PR #168's Notes section named several functions with a real
external-tool dependency that no `type -q`/`command -q`/`command -v`/
`which` guard covers anywhere in the tree, deliberately left out of
DEPENDENCIES to avoid breaking test_dependencies_resolve. Adds the
guard each was missing, then declares the dependency now that it
resolves:

- bkg, detach: nohup
- gitui: gitui (self-shadow; type -q -f to skip the function itself)
- play-media: mpv, vlc -- already guarded via `type -q -f $p` in a
  loop, just never recognized as one (see next point)
- steam-dl: systemd-inhibit, steam
- wake-lock: systemd-inhibit
- split, spwin, tab: wezterm, konsole (kitty already declared)

docs/verify-manual.py's guard-detection regex only matched `type -q
<name>` immediately, so `type -q -f $p` (the `-f` flag excludes
functions from the match, needed wherever a wrapper shadows a binary
of its own name) was invisible to it -- both as a direct guard and
through the loop-variable indirection. Broadened both patterns to
skip over any flags between `-q` and the name/variable.

split/spwin/tab dispatch on $TERM/$TERM_PROGRAM/$KONSOLE_VERSION to
pick which terminal-specific binary to call, per this repo's C4
convention -- but those env vars only prove the terminal type, not
that its CLI binary is on $PATH: they propagate over ssh, so sshing
out from Kitty/WezTerm inherits the var on a remote host that never
installed the binary. Same latent gap in clone/clonet, whose
clone-in-kitty is a function Kitty's own shell integration injects,
not present on a remote shell that only inherited $TERM. All five now
check the actual thing they are about to call, not just the env var
that selects it.

Also guards and documents three more real, previously-undeclared
dependencies found by the same audit, unrelated to PR #168's named
list but the identical pattern: fast-cli (fast), lock (loginctl),
ports (lsof), screensleep (busctl).

docs/fish-config.md regenerated to match.
rootiest added the Kind/BugArea/FunctionsArea/Docs labels 2026-09-23 02:41:17 +00:00
rootiest merged commit 9e8d29cc30 into main 2026-09-23 03:00:02 +00:00
rootiest deleted branch docs/guard-external-deps 2026-09-23 03:00:03 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rootiest/fish-config#170