@@ -2177,6 +2177,26 @@ static void main_daemon_ctl_open_browser(int port) {
21772177 }
21782178}
21792179
2180+ /* Ask the daemon to enable the UI on `port`; true when it accepted.
2181+ *
2182+ * The seam below forces the refusal that the caller must survive. Reproducing it
2183+ * for real needs a machine loaded enough to miss a bounded handshake, which is
2184+ * not a state a test can ask for; it is COMPILED OUT of ordinary builds (see
2185+ * TEST_SEAMS in Makefile.cbm). */
2186+ static bool main_daemon_ctl_apply_ui_config (cbm_daemon_runtime_client_t * client ,
2187+ uint8_t update_mask , int port ) {
2188+ #ifdef CBM_ENABLE_TEST_SEAMS
2189+ char forced [8 ];
2190+ if (cbm_safe_getenv ("CBM_TEST_DAEMON_UI_CONFIG_REFUSED" , forced , sizeof (forced ), NULL ) &&
2191+ forced [0 ] == '1' ) {
2192+ return false;
2193+ }
2194+ #endif
2195+ return cbm_daemon_application_client_set_ui_config (client , update_mask , true, port ,
2196+ MAIN_CONNECT_TIMEOUT_MS ) ==
2197+ CBM_DAEMON_RUNTIME_APPLICATION_OK ;
2198+ }
2199+
21802200static int main_daemon_ctl_finish_ui_open (cbm_daemon_runtime_client_t * * client_io , int port ,
21812201 bool open_browser ) {
21822202 if (!open_browser ) {
@@ -2395,6 +2415,7 @@ static int main_run_daemon_ctl(int argc, char **argv, const cbm_daemon_ipc_endpo
23952415 /* The committed control connection satisfied the daemon's no-client
23962416 * startup window; configure the UI before departing. */
23972417 int ui_port = 0 ;
2418+ bool ui_configured = false;
23982419 if ((CBM_EMBEDDED_FILE_COUNT > 0 )) {
23992420 cbm_ui_config_t ui_config ;
24002421 cbm_ui_config_load (& ui_config );
@@ -2403,14 +2424,30 @@ static int main_run_daemon_ctl(int argc, char **argv, const cbm_daemon_ipc_endpo
24032424 bool context_set =
24042425 main_set_client_context (start_result .client , "." , CBM_MCP_TOOL_PROFILE_ALL , NULL , NULL ,
24052426 MAIN_CONNECT_TIMEOUT_MS );
2406- if (!context_set || cbm_daemon_application_client_set_ui_config (
2407- start_result .client , update_mask , true, ui_port ,
2408- MAIN_CONNECT_TIMEOUT_MS ) != CBM_DAEMON_RUNTIME_APPLICATION_OK ) {
2427+ ui_configured = context_set &&
2428+ main_daemon_ctl_apply_ui_config (start_result .client , update_mask , ui_port );
2429+ if (!ui_configured ) {
2430+ /* Reaching here means the daemon is up: the control connection above
2431+ * already satisfied its startup window. Only the UI handshake — two
2432+ * requests bounded by MAIN_CONNECT_TIMEOUT_MS — came back short.
2433+ *
2434+ * Whether that is fatal depends on what was asked for. `--port`/`--open`
2435+ * make the UI the point of the command, so failing to configure it is a
2436+ * failed command. A bare `daemon start` asks for a daemon, and it got
2437+ * one; reporting failure there sent operators hunting for a daemon that
2438+ * was in fact running and healthy. It also made a loaded machine look
2439+ * like a broken one, because a second of scheduling delay after an
2440+ * abrupt shutdown is enough to miss this handshake. */
2441+ if (requested_port > 0 || open_browser ) {
2442+ (void )fprintf (stderr ,
2443+ "error: the daemon did not accept the UI configuration; browser was "
2444+ "not opened\n" );
2445+ (void )cbm_daemon_runtime_client_close (start_result .client , MAIN_CLOSE_TIMEOUT_MS );
2446+ return EXIT_FAILURE ;
2447+ }
24092448 (void )fprintf (stderr ,
2410- "error: the daemon did not accept the UI configuration; browser was "
2411- "not opened\n" );
2412- (void )cbm_daemon_runtime_client_close (start_result .client , MAIN_CLOSE_TIMEOUT_MS );
2413- return EXIT_FAILURE ;
2449+ "warning: the daemon started but did not accept the UI configuration; "
2450+ "the UI is unavailable until the next `daemon start`\n" );
24142451 }
24152452 } else if (requested_port > 0 || open_browser ) {
24162453 (void )fprintf (stderr , "warning: this binary was built without UI support; "
@@ -2427,8 +2464,10 @@ static int main_run_daemon_ctl(int argc, char **argv, const cbm_daemon_ipc_endpo
24272464 }
24282465 printf ("It survives idle periods and session ends; `codebase-memory-mcp daemon stop` "
24292466 "retires it.\n" );
2467+ /* Skipped when the handshake was refused: that path announces the port as
2468+ * warming, which would be a promise nothing is keeping. */
24302469 int ui_result =
2431- (CBM_EMBEDDED_FILE_COUNT > 0 )
2470+ (CBM_EMBEDDED_FILE_COUNT > 0 && ui_configured )
24322471 ? main_daemon_ctl_finish_ui_open (& start_result .client , ui_port , open_browser )
24332472 : EXIT_SUCCESS ;
24342473 if (start_result .client ) {
0 commit comments