Problem
Membership gossip has no convergence concept, and the leader acts without one. This is a documented design decision, not an accident — ClusterEvents.ts states it plainly: "There is no seenBy. […] gossip here merges member records individually, so there is no such version to have been seen." Records are merged one by one under scalar last-writer-wins on a wall-clock-seeded version; there is no notion of "the cluster has agreed on this view".
The consequence that makes it worth a tracked decision rather than a code comment: leader actions fire on the local view alone. onGossip promotes every joining/weakly-up member to up the moment isLeader() is true locally — and isLeader() itself is "lowest-addressed up member of my local map", so during any view divergence each side has a leader and each leader promotes. Everything built on membership inherits this: singleton placement (#949), shard allocation (#948), downing decisions (each strategy computes from the local view). Several filed bugs are, at root, instances of "two nodes acted on disagreeing views at the same time" — this issue is the umbrella decision about whether that stays a fact of the design or gets a convergence gate.
Options worth pricing (not mutually exclusive):
- Status quo, documented: keep per-record LWW, write the "leader may act on a divergent view" caveat into the cluster docs where singleton/sharding guarantees are stated, and lean on leases as the only uniqueness mechanism (the current de-facto answer).
- A seen-set gate for leader actions only: leader-side actions (promotion, allocation defaults) wait until the records they depend on have been echoed back by a quorum of reachable members — a lightweight convergence proxy without versioning the whole membership map.
- Versioned-view gossip: the full model — a vector-clocked membership document with a real seen-set. Largest change, retires the class.
Evidence
src/cluster/ClusterEvents.ts:44-46 — the design statement quoted above.
src/cluster/Cluster.ts:917-925 — promotion on the local view:
// Leader promotes joining (and weakly-up) members to up.
if (this.isLeader()) {
for (const member of this.members.values()) {
if (member.status === 'joining' || member.status === 'weakly-up') {
this.log.debug(`leader-promote: ${member.address} ${member.status}→up`);
this.updateMember(member.withStatus('up'));
}
}
}
Verification status
CONFIRMED-BY-READ — both sites quoted from the v0.15.0 tree. From the independent production-readiness pass (2026-08-14), second batch (design decisions). Related consequences already filed: #935 (merge tie-break), #930 (partition heal), #948/#949 (dual ownership), #1176 (island merge); #839/#838 cover the downing-side stability window and strategy selection.
Problem
Membership gossip has no convergence concept, and the leader acts without one. This is a documented design decision, not an accident —
ClusterEvents.tsstates it plainly: "There is noseenBy. […] gossip here merges member records individually, so there is no such version to have been seen." Records are merged one by one under scalar last-writer-wins on a wall-clock-seeded version; there is no notion of "the cluster has agreed on this view".The consequence that makes it worth a tracked decision rather than a code comment: leader actions fire on the local view alone.
onGossippromotes everyjoining/weakly-upmember toupthe momentisLeader()is true locally — andisLeader()itself is "lowest-addressed up member of my local map", so during any view divergence each side has a leader and each leader promotes. Everything built on membership inherits this: singleton placement (#949), shard allocation (#948), downing decisions (each strategy computes from the local view). Several filed bugs are, at root, instances of "two nodes acted on disagreeing views at the same time" — this issue is the umbrella decision about whether that stays a fact of the design or gets a convergence gate.Options worth pricing (not mutually exclusive):
Evidence
src/cluster/ClusterEvents.ts:44-46— the design statement quoted above.src/cluster/Cluster.ts:917-925— promotion on the local view:Verification status
CONFIRMED-BY-READ — both sites quoted from the v0.15.0 tree. From the independent production-readiness pass (2026-08-14), second batch (design decisions). Related consequences already filed: #935 (merge tie-break), #930 (partition heal), #948/#949 (dual ownership), #1176 (island merge); #839/#838 cover the downing-side stability window and strategy selection.