Skip to content

I3606 — Two live instances of one runtime slot keep their links, and the mesh names them instead of flapping

Stop two live server processes that share one runtime slot from evicting each other's link at every peer. A peer holds one inbound link per runtime key, so a hello from the other instance replaces the first instance's link, whose next sync is refused unknown_link and says hello again, every heartbeat, for as long as both run. Keep one inbound link per live instance of a runtime, count an alternation between two live instances as neither a restart nor a reconnect, and report the second instance on mesh peers and mesh doctor, naming both processes' endpoints.

READY wave 0 · p1 · implementation profile · parallel safe

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

Ready. Every dependency is done, so majordomus plan start I3606 will be accepted.

Objective

Stop two live server processes that share one runtime slot from evicting each other's link at every peer. A peer holds one inbound link per runtime key, so a hello from the other instance replaces the first instance's link, whose next sync is refused unknown_link and says hello again, every heartbeat, for as long as both run. Keep one inbound link per live instance of a runtime, count an alternation between two live instances as neither a restart nor a reconnect, and report the second instance on mesh peers and mesh doctor, naming both processes' endpoints.

Why

Measured live on 2026-10-20 on node 641bdb94: every local link of runtime b10f19aa2cd888a6 showed about 21,670 handshakes and 21,670 reconnects since 2026-10-19 12:09Z, about one per 5 s heartbeat, with 0 restarts. Two server processes of the primary checkout carry that runtime slot (instances 7eab159b6b89c7c5 and 8241af227eccef89, serving on 55765 and 8741; a dial of 127.0.0.1:8741 is refused self_link: that is this runtime). Every round re-signs a hello and a welcome per link, the reconnect counter no longer tells a dropping link from this, and each peer's view of the runtime alternates between two instances.

Current state

mesh/cooperation.rs keys PeerLink and table.inbound_links by runtime key. On a hello, a different card.instance counts a restart and clears the outbound link, and the old inbound link is dropped. On a welcome after the first, the dialer counts a reconnect. A sync on a dropped link is refused unknown_link and the dialer says hello at once (after_round). I3175 detects one node key on two hosts, not two processes of one runtime on one host.

Desired state

A peer keeps one inbound link per (runtime, instance) while both instances beat within the expiry. Neither instance re-handshakes on a steady link. mesh peers lists the runtime once, with its instances and their endpoints, and mesh doctor fails a check that names both, because one checkout should run one server (runtime-lifecycle). The reconnect counter counts only links that dropped.

Scope

  • apps/majordomus-cli/src/mesh/cooperation.rs
  • apps/majordomus-cli/src/mesh/doctor.rs
  • apps/majordomus-cli/tests/mesh_cooperation.rs
  • docs/MESH.md

Out of scope

  • Stopping either process: which server survives is the lifecycle's and the owner's decision
  • Two hosts presenting one node key, which is I3175

Dependencies

None. This issue is a root of the graph.

Acceptance criteria

  • Two instances of one runtime that both dial one peer each keep their link for ten heartbeats: zero unknown_link refusals, zero reconnects and zero restarts on either side
  • A real restart (an instance that stops and a new one that starts) is still counted as one restart
  • mesh peers names a runtime with two live instances once, listing both instances and their endpoints
  • mesh doctor fails a check naming the runtime and both instances while both beat, and passes once one stops

Validation

  • bin/majordomus plan validate
  • cargo test --lib mesh::cooperation
  • cargo test --test mesh_cooperation

Evidence required

  • two_instances_keep_their_links_test

Evidence

None recorded. Every token above needs a command or an artifact behind it before this issue can be completed; narrative is refused.

Timeline

started
—
verified
—
completed
—

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

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