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
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
| covers | type | command | result | at commit |
|---|---|---|---|---|
| stable_port_per_checkout_test | test | cargo test --test serve_lifecycle; bash test/run.sh --no-skips 190_entry_is_automatic | serve_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.