I2163 — A client never attaches to a server of another version without saying so, and a newer client replaces an older server
Make the election compare versions through the one standing verdict: a newer client takes over in an ordered hand-off, an older client attaches and says it is older.
DONE wave 1 · p1 · implementation profile · runs alone
Objective
Make the election compare versions through the one standing verdict: a newer client takes over in an ordered hand-off, an older client attaches and says it is older.
Why
lease::elect and ask compare name, repository_id and leaseholder, never the version (apps/majordomus-cli/src/lease.rs:896-920). The superseded check covers only a binary replaced at the same path (apps/majordomus-cli/src/lease.rs:695-709); the installer puts each version in its own directory under ~/.local/share/majordomus/versions/<v>/, so an upgrade changes the path and nothing is superseded. A new client then attaches to the older server, and the mismatch is reported only as an Outdated warning by server::standing_at (apps/majordomus-cli/src/capability/builtin/server.rs:410-424, apps/majordomus-cli/src/capability/builtin/health.rs:606), which MCP clients never see. docs/MCP.md:49 presents supersession as covering yesterday's code. Baseline audit of 2026-10-09 at master 1fb8d6be44, failure gap 3, premise 3 and drift 7.
Current state
After an upgrade through the installer, new clients are served by the old version until it idles out.
Desired state
ask treats a lease whose version differs from crate::VERSION through the verdict server::standing_at already gives: a newer client takes over in an ordered hand-off, the old server's sessions moving on as I2127 makes them; an older client attaches and reports the difference in its initialize answer; docs/MCP.md says what each case does.
Scope
- apps/majordomus-cli/src/lease.rs
- apps/majordomus-cli/src/capability/builtin/server.rs
- apps/majordomus-cli/src/mcp/protocol.rs
- apps/majordomus-cli/src/commands/serve.rs
- apps/majordomus-cli/tests/serve_lifecycle.rs
- test/cases
- docs/MCP.md
Dependencies
Acceptance criteria
- A client of version N+1 started beside a server of version N installed at another path ends with one server of N+1 and the old server's sessions re-attached
- A client of version N-1 attaching to a server of version N reports the difference in its initialize answer
- One function decides the version verdict for elect, serve ensure and server.status
Validation
- bin/majordomus plan validate
- cargo test --test serve_lifecycle --test server_status
- MJ_TEST_JOBS=4 bash test/run.sh <the case this issue adds>
Evidence required
- newer_client_replaces_older_server_test
- older_client_told_test
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| newer_client_replaces_older_server_test | ci | gh run view 38626102499 --log --job 116065669794 (validate on master e8505a431d, rust (3): cargo test --test serve_lifecycle) | tests/serve_lifecycle.rs newer_client_replaces_older_server_test ... ok and an_older_client_leaves_a_newer_server_alone_and_says_so ... ok in validate run 38626102499 (success) at e8505a431d; delivered by 383309ae06c | c0e3585 |
| older_client_told_test | ci | gh run view 38626102499 --log --job 116065669775 (validate on master e8505a431d, rust (2): cargo test --lib) | commands::mcp::tests::older_client_told_test ... ok in validate run 38626102499 (success) at e8505a431d; delivered by 383309ae06c | c0e3585 |
Timeline
- started
- 2026-10-14T22:14:26Z
- verified
- 2026-10-14T22:14:44Z
- completed
- 2026-10-14T22:15:01Z
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/I2163.yaml. Read it back with majordomus plan show I2163.