Skip to content

I2702 — A checkout keeps one address across restarts, because its server binds a port derived from the checkout

Bind a port derived from the repository and worktree identity before falling back to a free one, so that a Streamable HTTP client configuration or a bookmarked UI names an address that survives a restart.

DONE wave 1 · 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

Bind a port derived from the repository and worktree identity before falling back to a free one, so that a Streamable HTTP client configuration or a bookmarked UI names an address that survives a restart.

Why

Split from I2131 on 2026-10-14. I2131 made every surface's use count toward the idle timeout, and its desired_state said 'Not done here' of a port derived from the repository and worktree identity, while its acceptance criteria still required it, so it could never close. On 2026-10-09 the live server asked for 8741 and served 61068, an address that changes on every restart. The other deferred question of I2131, whether an entry beyond serve ensure is needed, is decided by ADR 0135 and planned as I2484, and is not carried here. Baseline audit of 2026-10-09 at master 1fb8d6be44, lifecycle gap 9 and drift 10.

Current state

DEFAULT_PORT is 8741 (apps/majordomus-cli/src/cli.rs); the first checkout to start takes it and every other is given a free port when http::server::bind (apps/majordomus-cli/src/http/server.rs) cannot have the one asked for, a different one on each restart. docs/MCP.md says a taken port is replaced by a free one and shows :8741 in its examples.

Desired state

A server asks first for a port derived from the repository identity and the worktree it serves, so one checkout asks for the same port on every start and two checkouts of one repository ask for different ones; a port that is taken falls back to a free one and both are logged, as today. docs/MCP.md describes the derived port and the fallback.

Scope

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

Out of scope

  • The idle timer and what counts as use, which I2131 delivered
  • A majordomus ui command, which ADR 0135 decided and I2484 plans

Dependencies

Acceptance criteria

  • Two consecutive starts of one checkout's server bind the same port when it is free
  • Two checkouts of one repository derive different ports
  • docs/MCP.md describes the derived port and the fallback

Validation

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

Evidence required

  • stable_port_per_checkout_test

Evidence

coverstypecommandresultat commit
stable_port_per_checkout_testtestcargo test --test serve_lifecycle; bash test/run.sh --no-skips 190_entry_is_automaticserve_lifecycle 17 passed incl. stable_port_per_checkout_test; case 190 ok, 0 skipped (code on master since b3f35a34a5f; re-run at master 01227a0)01227a0

Timeline

started
2026-10-17T12:02:17Z
verified
2026-10-17T12:02:47Z
completed
2026-10-17T12:02:56Z

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/I2702.yaml. Read it back with majordomus plan show I2702.

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