fix(cli): a refused UI configuration is not a failed daemon start - #1796
fix(cli): a refused UI configuration is not a failed daemon start#1796liuchong wants to merge 1 commit into
Conversation
`daemon start` exited nonzero when the daemon came up but did not answer the UI configuration handshake. By that point the daemon is running -- the control connection above it already satisfied the startup window -- so the command reported failure for something it had already achieved. The two requests in that handshake are bounded at MAIN_CONNECT_TIMEOUT_MS, one second. On a loaded machine, especially right after an abrupt shutdown, that is thin. Windows CI hit it repeatedly: the crash-recovery section of `tests/windows/test_daemon_stability.py` hard-kills a daemon and immediately starts a fresh one, and the report that came back was that no fresh daemon had started -- when one had, and was serving. Whether the refusal is fatal now depends on what was asked for. `--port` and `--open` make the UI the point of the command and keep the existing nonzero exit. A bare `daemon start` asks for a daemon and got one, so it now warns and succeeds; it also skips the UI-warming notice, which would otherwise announce a port that nothing is serving. A seam forces the refusal, because reproducing it for real needs a machine loaded enough to miss a bounded handshake -- not a state a test can ask for. The lifecycle guard covers both halves of the contract, and with the decision reverted it reports the bare-start case as red. Signed-off-by: 刘冲 <mail@liuchong.dev>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
|
Thank you for isolating this downstream startup result and for using a deterministic test seam instead of a timing-dependent test. I checked current I have labeled this as a high-priority stability bug and routed it for review. The current CI surface is green. Our review queue is full, so detailed review may take a little time. Thank you for keeping the proposed behavior split at the command contract rather than simply extending the timeout. |
|
Understood, and nothing needed from me here. One thing worth stating so you do not have to ask: when this merges I will rebase #1723–#1728 onto it right away. That stack is where the defect surfaced — Take the review time you need. |
The bug
daemon startreports failure for a daemon that started and is serving.test / test-windows-guardsgoes red with:That message has exactly one source, in
main_run_daemon_ctl. Reaching it means the daemon is already up — the comment directly above it says so, and the handshake is sent over the very control connection that satisfied the daemon's no-client startup window. Only the UI configuration came back short.Root cause
The handshake is two requests, each bounded by
MAIN_CONNECT_TIMEOUT_MS(one second), and missing that window was treated as a failed start.One second is thin on a loaded machine, and
section_crash_recoverymanufactures exactly that state: it hard-kills a daemon and immediately starts a fresh one, on a runner still reclaiming the dead process's resources. This is the same environment #1772 addressed, but a different bounded wait — #1772 fixed the startup-transition lock, and this path sits downstream of it. The failure reproduces on a branch that already carries #1772.The change
Whether a refused UI configuration is fatal now depends on what was asked for.
--portand--openmake the UI the point of the command, so they keep the existing nonzero exit. A baredaemon startasks for a daemon and got one, so it warns and succeeds. It also skips the UI-warming notice, which would otherwise announce a port that nothing is serving.Verification
A seam (
CBM_TEST_DAEMON_UI_CONFIG_REFUSED, compiled out withoutTEST_SEAMS=1) forces the refusal, because reproducing it for real needs a machine loaded enough to miss a bounded handshake — not a state a test can ask for.tests/windows/test_daemon_lifecycle.pycovers both halves of the contract and reports a precondition skip against a non-UI binary. Checked locally against ascripts/build.sh --with-ui TEST_SEAMS=1build:a refused UI configuration must not fail daemon startlint-formatandlint-cppcheckare clean.The underlying timing only occurs on Windows CI, so the guard's behaviour there is what this PR is asking to confirm.