Skip to content

I2126 — A server busy with tool calls is never taken over as if it were dead

Make the lease probe reach a server whose HTTP workers are all running tool calls, and make it prove it reached the leaseholder, so that a client deciding whether the owner is alive is never answered by a full worker pool or by another process.

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

Make the lease probe reach a server whose HTTP workers are all running tool calls, and make it prove it reached the leaseholder, so that a client deciding whether the owner is alive is never answered by a full worker pool or by another process.

Why

The probe a client sends before taking over is GET / (apps/majordomus-cli/src/lease.rs:896-925), served by the same four workers (apps/majordomus-cli/src/http/server.rs:23) that run each tools/call synchronously. Four tool calls that outlast the probe timeout plus the busy grace (about 12 s, apps/majordomus-cli/src/lease.rs:43-62, 555-568) make a healthy server look wedged, and the next client to start takes its lease. is_own (apps/majordomus-cli/src/lease.rs:429) fixes only a server probing itself. GET / is also the most expensive route: it resolves the index and every surface, while /api/v1/live and /api/v1/ready exist (apps/majordomus-cli/src/capability/builtin/health.rs:691,702) and the probe does not use them. The probe compares name, repository_id and leaseholder (apps/majordomus-cli/src/lease.rs:902-905) but not the lease token, pid or started_at, and GET / does not expose them. Baseline audit of 2026-10-09 at master 1fb8d6be44, lifecycle gap 3 and the fencing row.

Current state

apps/majordomus-cli/tests/lease_busy.rs covers a live owner that answers late and one that never answers; no test holds every worker busy while a second client elects, and no probe compares the token.

Desired state

The workers only accept; each request is answered on a thread of its own up to MAX_HANDLERS (64), so a lease probe is answered while slow tool calls run, and stop waits a bounded time for handed-off requests. The deferred part, a liveness probe route carrying a digest of the lease token and the check that refuses a server whose token differs, is I2700.

Scope

  • apps/majordomus-cli/src/lease.rs
  • apps/majordomus-cli/src/http/server.rs
  • apps/majordomus-cli/src/http/router.rs
  • apps/majordomus-cli/src/capability/builtin/health.rs
  • apps/majordomus-cli/tests/lease_busy.rs
  • test/cases
  • docs/MCP.md

Dependencies

None. This issue is a root of the graph.

What waits on this

Acceptance criteria

  • With four tool calls in flight that each outlast the probe timeout plus the busy grace, a fifth client attaches to the running server and the lease file is unchanged

Validation

  • bin/majordomus plan validate
  • cargo test --test lease_busy --test mcp_shared

Evidence required

  • busy_pool_is_not_taken_over_test

Evidence

coverstypecommandresultat commit
busy_pool_is_not_taken_over_testtestcargo test --locked --manifest-path apps/majordomus-cli/Cargo.toml --test http_busy_probe busy_pool_is_not_taken_over_testtest 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:43Z
verified
—
completed
2026-10-14T12:16:23Z

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

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