lock_fd was initialised to 0, but every other use in the class treats a
negative value as "no lock held": openComms() sets it to -1 when open() or
flock() fails, and closeComms() guards on `lock_fd >= 0` before closing.
The constructor's 0 passes that guard, so a StreamBase destructed without
openComms() having succeeded calls close(0) and closes stdin. Reaching it only
takes connkey > 0 plus a runStream() that returns before openComms(), and
MonitorStream::runStream() has two such returns: the STREAM_SINGLE branch and
the !monitor branch. mode=single URLs still carry a connkey, so both are
reachable in normal operation.
zms exits shortly afterwards and does little in between, so the practical
impact today is small. It is still closing a descriptor the class does not own,
and once fd 0 is free the next open() in the process silently lands on it.
Add a regression test covering destruction with and without a connkey. It saves
and restores fd 0 so a failure can't cascade into the rest of the suite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>