Skip to content

I2131 — A server in use through the Cockpit or HTTP is not stopped as idle, and a checkout keeps one address

Count every surface's use toward the idle timeout, and bind a port derived from the checkout's identity before falling back to a random one.

DONE wave 0 · p1 · implementation profile · runs alone

Part of runtime-lifecycle — One usable server per checkout starts, attaches, stops and reports its state without races or hidden kills.

Objective

Count every surface's use toward the idle timeout, and bind a port derived from the checkout's identity before falling back to a random one.

Why

'Attached' means MCP HTTP sessions only (apps/majordomus-cli/src/shared.rs:250-253), and the serve --idle loop stops after 900 s without one (apps/majordomus-cli/src/commands/serve.rs:128-146). A person using only the Cockpit, REST or the /events WebSocket is not counted, so a UI-first server stops 15 minutes into being viewed. The default port 8741 (apps/majordomus-cli/src/cli.rs:3301) goes to one checkout and every other one falls back to a random port (apps/majordomus-cli/src/http/server.rs:77-86): on 2026-10-09 the live server asked for 8741 and serves 61068, an address that changes on every restart, so a Streamable HTTP client configuration or a bookmarked Cockpit has nothing stable to name. docs/MCP.md:29 shows :8741 as if it were the norm. There is no majordomus ui command; serve ensure and env enter are the UI-first entries (apps/majordomus-cli/src/commands/serve.rs:481-576, apps/majordomus-cli/src/commands/env.rs:701-760) and work without stdio (case 190_entry_is_automatic.sh). Baseline audit of 2026-10-09 at master 1fb8d6be44, lifecycle gap 9 and drift 10.

Current state

A server with no MCP session stops at its idle timeout whatever else uses it. Every checkout but one serves on a random port.

Desired state

The idle timer counts as use an MCP session attached, an HTTP request answered within the last second (http::server::used_within) and an open /events channel, so the Cockpit, the REST API and a watched execution keep their server. The deferred part, a port derived from the repository and worktree identity, is I2702; whether an entry beyond serve ensure is needed is decided by ADR 0135 and planned as I2484.

Scope

  • apps/majordomus-cli/src/shared.rs
  • apps/majordomus-cli/src/commands/serve.rs
  • apps/majordomus-cli/src/http/server.rs
  • apps/majordomus-cli/src/http/events.rs
  • apps/majordomus-cli/src/cli.rs
  • apps/majordomus-cli/tests/serve_lifecycle.rs
  • docs/MCP.md
  • docs/COCKPIT.md

Out of scope

  • Adding a majordomus ui command before this issue's record decides one is needed.

Dependencies

None. This issue is a root of the graph.

What waits on this

Acceptance criteria

  • A server with an open /events WebSocket and no MCP session is still running after its idle timeout, and stops within one timeout of the socket closing

Validation

  • bin/majordomus plan validate
  • cargo test --test serve_lifecycle
  • MJ_TEST_JOBS=4 bash test/run.sh 190_entry_is_automatic

Evidence required

  • ui_use_keeps_server_alive_test

Evidence

coverstypecommandresultat commit
ui_use_keeps_server_alive_testtestcargo test --locked --manifest-path apps/majordomus-cli/Cargo.toml --test serve_in_use a_server_watched_over_its_live_channel_outlives_its_idle_timeout_and_ends_once_it_closestest result: ok. 2 passed; 0 failed (whole file) (run 2026-10-14 12:00-12:15Z on master 2ca25bb9cf plus this change)2ca25bb

Timeline

started
2026-10-14T12:15:51Z
verified
—
completed
2026-10-14T12:16:31Z

Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.

Canonical record: .ai/repo/project/issues/I2131.yaml. Read it back with majordomus plan show I2131.

Searches the titles and descriptions of every page this site publishes.