Summary
trap -p omits signals that were already ignored (SIG_IGN) when the shell started. bash reports these as trap -- '' SIGNAME; brush prints nothing for them.
This is what makes the OS target tests matrix red. All six containers (Arch, Azure Linux 4.0, Debian testing, Fedora, NixOS, openSUSE Tumbleweed) fail on exactly one case, [trap -p - with no args shows all traps], and always with the same diff:
stdout DIFFERS:
------ Oracle <> Test: stdout ---------------------------------
- trap -- '' SIGRTMIN
trap -- 'echo a' SIGINT
trap -- 'echo b' SIGTERM
trap -- 'echo c' EXIT
Those containers start the shell with SIGRTMIN inherited as ignored — glibc/systemd reserve the first real-time signals — so the distro bash acting as oracle lists it and brush does not.
| job |
ran |
succeeded |
failed |
| Arch Linux |
2189 |
1809 |
1 |
| Azure Linux 4.0 |
2185 |
1805 |
1 |
| Debian testing |
2189 |
1809 |
1 |
| Fedora |
2187 |
1807 |
1 |
| NixOS |
2187 |
1807 |
1 |
| openSUSE Tumbleweed |
2188 |
1808 |
1 |
The Ubuntu-based Test (linux/*) jobs pass because nothing ignores SIGRTMIN there, so the missing line never appears in the oracle's output.
Not actually about SIGRTMIN
The real-time signal is incidental. The same gap reproduces with an ordinary signal — ignore it in a parent and inspect the child:
$ bash -c "trap '' SIGUSR1; exec bash -c 'trap -p'"
trap -- '' SIGUSR1
$ bash -c "trap '' SIGUSR1; exec brush -c 'trap -p'"
# nothing
So this is "brush does not track signals ignored at entry", not "brush lacks SIGRTMIN".
Three separable gaps
Splitting these out because only the first is needed to turn CI green, and they can land independently.
1. Signals ignored at entry are not recorded or reported. Shown above. This alone is what the failing test observes.
2. Signals ignored at entry are not protected from being trapped. POSIX and bash both hold that a signal ignored on entry to the shell cannot be trapped or reset — bash silently declines, brush installs the handler:
$ bash -c "trap '' SIGUSR1; exec bash -c \"trap 'echo caught' SIGUSR1; trap -p\""
trap -- '' SIGUSR1 # refused; still ignored
$ bash -c "trap '' SIGUSR1; exec brush -c \"trap 'echo caught' SIGUSR1; trap -p\""
trap -- 'echo caught' SIGUSR1 # accepted
This one has practical consequences beyond trap -p output: a script run under nohup, or from a supervisor that ignores a signal, can install a handler in brush that bash would have refused, so the two shells genuinely behave differently at runtime.
3. Real-time signals are not known at all. SIGRTMIN, SIGRTMAX, the SIGRTMIN+n/SIGRTMAX-n forms, and the corresponding numbers are all rejected:
$ brush -c "trap 'echo x' SIGRTMIN"
error: trap: SIGRTMIN: invalid signal specification # exit 1
$ brush -c "trap 'echo x' 34"
error: trap: 34: invalid signal specification # exit 1
kill -l agrees — comparing the name sets, brush is missing exactly 31 entries and has no spurious ones:
SIGRTMIN SIGRTMIN+1 .. SIGRTMIN+15 SIGRTMAX SIGRTMAX-1 .. SIGRTMAX-14
(31 names in bash's list, 62 total vs brush's 31.) Worth noting these are Linux-specific and the SIGRTMIN+n names are relative to a value that varies by libc, so this is the least portable of the three and probably wants to be #[cfg]-gated.
Suggested fix for (1)
At startup, query the disposition of each known signal and record the ones already set to SIG_IGN, so trap -p can report them. On unix, sigaction(2) with a null action reads the current handler without installing one. That is enough to make the six OS target tests jobs green; (2) and (3) can follow separately.
Reproducing without a container
$ bash -c "trap '' SIGUSR1; exec brush -c 'trap -p'" # expect: trap -- '' SIGUSR1
Found while running a ~95k-line real-world bash script under brush. Not a blocker for that work — filing it because it is currently the only thing standing between the OS target tests matrix and green, and it was initially easy to mistake for a CI infrastructure problem.
Summary
trap -pomits signals that were already ignored (SIG_IGN) when the shell started. bash reports these astrap -- '' SIGNAME; brush prints nothing for them.This is what makes the
OS target testsmatrix red. All six containers (Arch, Azure Linux 4.0, Debian testing, Fedora, NixOS, openSUSE Tumbleweed) fail on exactly one case,[trap -p - with no args shows all traps], and always with the same diff:Those containers start the shell with
SIGRTMINinherited as ignored — glibc/systemd reserve the first real-time signals — so the distro bash acting as oracle lists it and brush does not.The Ubuntu-based
Test (linux/*)jobs pass because nothing ignoresSIGRTMINthere, so the missing line never appears in the oracle's output.Not actually about SIGRTMIN
The real-time signal is incidental. The same gap reproduces with an ordinary signal — ignore it in a parent and inspect the child:
So this is "brush does not track signals ignored at entry", not "brush lacks SIGRTMIN".
Three separable gaps
Splitting these out because only the first is needed to turn CI green, and they can land independently.
1. Signals ignored at entry are not recorded or reported. Shown above. This alone is what the failing test observes.
2. Signals ignored at entry are not protected from being trapped. POSIX and bash both hold that a signal ignored on entry to the shell cannot be trapped or reset — bash silently declines, brush installs the handler:
This one has practical consequences beyond
trap -poutput: a script run undernohup, or from a supervisor that ignores a signal, can install a handler in brush that bash would have refused, so the two shells genuinely behave differently at runtime.3. Real-time signals are not known at all.
SIGRTMIN,SIGRTMAX, theSIGRTMIN+n/SIGRTMAX-nforms, and the corresponding numbers are all rejected:kill -lagrees — comparing the name sets, brush is missing exactly 31 entries and has no spurious ones:(31 names in bash's list, 62 total vs brush's 31.) Worth noting these are Linux-specific and the
SIGRTMIN+nnames are relative to a value that varies by libc, so this is the least portable of the three and probably wants to be#[cfg]-gated.Suggested fix for (1)
At startup, query the disposition of each known signal and record the ones already set to
SIG_IGN, sotrap -pcan report them. On unix,sigaction(2)with a null action reads the current handler without installing one. That is enough to make the sixOS target testsjobs green; (2) and (3) can follow separately.Reproducing without a container
$ bash -c "trap '' SIGUSR1; exec brush -c 'trap -p'" # expect: trap -- '' SIGUSR1Found while running a ~95k-line real-world bash script under brush. Not a blocker for that work — filing it because it is currently the only thing standing between the
OS target testsmatrix and green, and it was initially easy to mistake for a CI infrastructure problem.