Renewal failed with an empty error box for any CN listed in index.txt
without a corresponding pki/issued/<CN>.crt — the state you get when an
index.txt is carried over from an older EasyRSA install but the issued/
files are not.
Two defects:
1. EasyRSA writes its diagnostics to stdout, not stderr: print() is
`printf '%s\n'`, and both die() and user_error() route through it.
stderr only carries output from the tools EasyRSA shells out to, and
even that is silenced under -S/--silent-ssl. _run_easyrsa built its
message from stderr alone, so every EasyRSA failure reported blank.
_easyrsa_diagnostics() now merges both streams (stderr first, so the
specific openssl message is not what the dialog clips) and drops the
version banner and blank padding.
2. EasyRSA reads the serial out of the .crt itself, so revoke-issued
cannot revoke a CN whose cert file is gone — and there is nothing to
add to the CRL either. has_issued_cert() now gates the revoke and CRL
steps in both CliRunner._issue() and CursesApp._process_cert(); the
workflow warns and goes straight to build-client-full. An explicit
--revoke / TUI `r` still fails loudly rather than silently no-op.
The post-build failure message now keys off whether a revoke actually
happened, not off is_renewal, so it no longer claims "has been revoked"
when nothing was.
Adds tests/test_app_reissue.py: the TUI re-issue path had no coverage at
all, and it is the path this bug was reported from.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>