# Invariant: a leadership-transfer election overrides a healthy leader lease. The transfer target
# campaigns as a forced REAL candidate whose RequestVote carries leader_transfer=true; a voter grants
# it even though it has heard from the current leader within its election timeout (CheckQuorum's
# anti-disruption lease). Mirrors openraft's leadership-transfer vote overriding the leader lease.
#
# Non-vacuity: CheckQuorum is ENABLED and the clock never advances past the followers' election
# deadline, so the other voter (node 3) holds a live lease on leader node 1 throughout. Under that
# lease a normal higher-term challenger would be ignored (see prevote_does_not_disrupt_live_leader /
# checkquorum). The transfer still elects node 2 only because the forced vote bypasses the lease — a
# build that ignored leader_transfer would leave node 3 lease-bound, deny the vote, and the transfer
# would stall with node 2 stuck as a candidate.
add-nodes 3 voters=(1,2,3) checkquorum=true
----
n1: created voters={1,2,3} term=0 commit=0 last=0
n2: created voters={1,2,3} term=0 commit=0 last=0
n3: created voters={1,2,3} term=0 commit=0 last=0

campaign 1
----
n1 candidate term=1
RequestVote term=1 last=0/0 prevote=false
RequestVote term=1 last=0/0 prevote=false

stabilize
----
> n2 recv RequestVote term=1 last=0/0 prevote=false
> n3 recv RequestVote term=1 last=0/0 prevote=false
> n2 ready
  VoteResponse term=1 prevote=false reject=false
> n3 ready
  VoteResponse term=1 prevote=false reject=false
> n1 recv VoteResponse term=1 prevote=false reject=false
> n1 recv VoteResponse term=1 prevote=false reject=false
> n1 ready
  Heartbeat term=1 commit=0
  Heartbeat term=1 commit=0
  AppendEntries term=1 prev=0/0 commit=0 entries=[1/1 empty]
  AppendEntries term=1 prev=0/0 commit=0 entries=[1/1 empty]
  n1 leader-changed term=1 lead=1
> n2 recv Heartbeat term=1 commit=0
> n3 recv Heartbeat term=1 commit=0
> n2 recv AppendEntries term=1 prev=0/0 commit=0 entries=[1/1 empty]
> n3 recv AppendEntries term=1 prev=0/0 commit=0 entries=[1/1 empty]
> n2 ready
  HeartbeatResponse term=1
  AppendResponse term=1 match=1
  n2 leader-changed term=1 lead=1
> n3 ready
  HeartbeatResponse term=1
  AppendResponse term=1 match=1
  n3 leader-changed term=1 lead=1
> n1 recv HeartbeatResponse term=1
> n1 recv AppendResponse term=1 match=1
> n1 recv HeartbeatResponse term=1
> n1 recv AppendResponse term=1 match=1
> n1 ready
  AppendEntries term=1 prev=0/0 commit=0 entries=[1/1 empty]
  AppendEntries term=1 prev=1/1 commit=1 entries=[]
  AppendEntries term=1 prev=0/0 commit=1 entries=[1/1 empty]
  AppendEntries term=1 prev=1/1 commit=1 entries=[]
> n2 recv AppendEntries term=1 prev=0/0 commit=0 entries=[1/1 empty]
> n2 recv AppendEntries term=1 prev=1/1 commit=1 entries=[]
> n3 recv AppendEntries term=1 prev=0/0 commit=1 entries=[1/1 empty]
> n3 recv AppendEntries term=1 prev=1/1 commit=1 entries=[]
> n2 ready
  AppendResponse term=1 match=1
  AppendResponse term=1 match=1
> n3 ready
  AppendResponse term=1 match=1
  AppendResponse term=1 match=1
> n1 recv AppendResponse term=1 match=1
> n1 recv AppendResponse term=1 match=1
> n1 recv AppendResponse term=1 match=1
> n1 recv AppendResponse term=1 match=1

# Node 1 leads; nodes 2 and 3 have heard from it and hold a live CheckQuorum lease on it.
raft-state
----
n1: leader term=1 lead=1
n2: follower term=1 lead=1
n3: follower term=1 lead=1

# Transfer leadership 1 -> 2. The clock is not advanced, so nodes' leases on node 1 stay fresh.
transfer-leadership from=1 to=2
----
TimeoutNow term=1

stabilize
----
> n2 recv TimeoutNow term=1
> n2 ready
  RequestVote term=2 last=1/1 prevote=false
  RequestVote term=2 last=1/1 prevote=false
  n2 leader-changed term=2 lead=none
> n1 recv RequestVote term=2 last=1/1 prevote=false
> n3 recv RequestVote term=2 last=1/1 prevote=false
> n1 ready
  VoteResponse term=2 prevote=false reject=false
  n1 leader-changed term=2 lead=none
> n3 ready
  VoteResponse term=2 prevote=false reject=false
  n3 leader-changed term=2 lead=none
> n2 recv VoteResponse term=2 prevote=false reject=false
> n2 recv VoteResponse term=2 prevote=false reject=false
> n2 ready
  Heartbeat term=2 commit=0
  Heartbeat term=2 commit=0
  AppendEntries term=2 prev=1/1 commit=1 entries=[2/2 empty]
  AppendEntries term=2 prev=1/1 commit=1 entries=[2/2 empty]
  n2 leader-changed term=2 lead=2
> n1 recv Heartbeat term=2 commit=0
> n3 recv Heartbeat term=2 commit=0
> n1 recv AppendEntries term=2 prev=1/1 commit=1 entries=[2/2 empty]
> n3 recv AppendEntries term=2 prev=1/1 commit=1 entries=[2/2 empty]
> n1 ready
  HeartbeatResponse term=2
  AppendResponse term=2 match=2
  n1 leader-changed term=2 lead=2
> n3 ready
  HeartbeatResponse term=2
  AppendResponse term=2 match=2
  n3 leader-changed term=2 lead=2
> n2 recv HeartbeatResponse term=2
> n2 recv AppendResponse term=2 match=2
> n2 recv HeartbeatResponse term=2
> n2 recv AppendResponse term=2 match=2
> n2 ready
  AppendEntries term=2 prev=1/1 commit=1 entries=[2/2 empty]
  AppendEntries term=2 prev=2/2 commit=2 entries=[]
  AppendEntries term=2 prev=1/1 commit=2 entries=[2/2 empty]
  AppendEntries term=2 prev=2/2 commit=2 entries=[]
> n1 recv AppendEntries term=2 prev=1/1 commit=1 entries=[2/2 empty]
> n1 recv AppendEntries term=2 prev=2/2 commit=2 entries=[]
> n3 recv AppendEntries term=2 prev=1/1 commit=2 entries=[2/2 empty]
> n3 recv AppendEntries term=2 prev=2/2 commit=2 entries=[]
> n1 ready
  AppendResponse term=2 match=2
  AppendResponse term=2 match=2
> n3 ready
  AppendResponse term=2 match=2
  AppendResponse term=2 match=2
> n2 recv AppendResponse term=2 match=2
> n2 recv AppendResponse term=2 match=2
> n2 recv AppendResponse term=2 match=2
> n2 recv AppendResponse term=2 match=2

# Node 2 won at term 2: node 3 granted the forced vote despite its live lease on node 1, and node 1
# stepped down. A lease-honoring (non-override) build would leave node 2 a stuck candidate.
raft-state
----
n1: follower term=2 lead=2
n2: leader term=2 lead=2
n3: follower term=2 lead=2

# The new leader's term-2 entry is committed and applied cluster-wide — the transfer fully completed.
raft-log 2
----
n2: commit=2 applied=2 last=2
  1/1 empty
  2/2 empty

raft-log 3
----
n3: commit=2 applied=2 last=2
  1/1 empty
  2/2 empty

