Prove2Me
Navigate
DiscoverCollectionsFormalpediaGalaxyBlogsUsersMomentumMy Missions+
Prove2Me
Log in
← Formalpedia

Every publication descends from a first publication of its term

Proved
OpenSearchSafety.inv_reign_first

by pitimespi · Nov 1, 2026 · Mathlib 0df444a (Lean v4.33.1)

cluster-coordinationconsensuscrash-recoveryelasticsearchformal-verificationinductive-invariantleader-electionmessage-passingopensearchpaxosraftreconfigurationsafetyzen2

For all P, every cluster W with Inv P W and every sent publication W.sent (.publishRequest src s pt pv), there is a sent publication W.sent (.publishRequest src' s' pt' pv') with s'.term = s.term, pt' < s'.term and Descends W s'.term s'.version s.term s.version. The first publication of a term is the one whose parent is of an older term: the state its publisher had accepted when it won the election.

Preamble
import Definitions.Def_OpenSearchSafety
open OpenSearchCoordination OpenSearchSafety
Formal statement
theorem OpenSearchSafety.inv_reign_first {V : Type} (P : Params) (W : Cluster V) (hinv : Inv P W)
    (src : NodeId) (s : ClusterState V) (pt : Nat) (pv : Int) (hs : W.sent (.publishRequest src s pt pv)) :
    ∃ src' s' pt' pv', W.sent (.publishRequest src' s' pt' pv') ∧ s'.term = s.term ∧ pt' < s'.term ∧
      Descends W s'.term s'.version s.term s.version := by sorry
Source
OpenSearch b2a54ba5c0defba3d1d20423e911d4bf920bab19: CoordinationState (the per-node handlers as transcribed by the OpenSearchCoordination mission; handleCommit lines 523-573, isPublishQuorum 156-158, invariant 601-611): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java; Coordinator (the calls into CoordinationState: handleApplyCommit lines 419-452, handlePublishRequest 454-526, startElection 583-602, abdicateTo 604-620, ensureTermAtLeast 635-641, joinLeaderInTerm 643-657, setInitialConfiguration 1101-1161, handleJoin 1267-1299, publish 1350 with the handleClientValue call at 1404, CoordinatorPublication.handlePublishResponse 1871-1876): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java; ClusterBootstrapService (requirements 146-157, onFoundPeersUpdated 176-215, startBootstrap 259-273, checkRequirements 306-335): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/ClusterBootstrapService.java#L146-L335; GatewayMetaState.start (persisted state loaded and rebuilt 141-231, remote restore 169-188, synchronous and asynchronous persistence 199-207, nodes without the cluster-manager and data roles 232-255): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/gateway/GatewayMetaState.java#L126-L257; PersistedStateRegistry.addPersistedState (lines 38-41): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/PersistedStateRegistry.java#L38-L41; ElectionStrategy (DEFAULT_INSTANCE lines 46-59): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/ElectionStrategy.java#L44-L110; the randomized safety test CoordinationStateTestCluster (reboot 156-193, runRandomly 279-329, invariant 331-370): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/test/framework/src/main/java/org/opensearch/cluster/coordination/CoordinationStateTestCluster.java and CoordinationStateTests.testSafety (lines 917-924): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/test/java/org/opensearch/cluster/coordination/CoordinationStateTests.java#L917-L924; for comparison, Elastic's TLA+ model ZenWithTerms (HandleCommit 241-250, CommittedValuesDescendantsFromInitialValue 338-347, CommitHasQuorumVsPreviousCommittedConfiguration 349-358): https://github.com/elastic/elasticsearch-formal-models/blob/ca30663506a7e18de9d23fabbc70a369903d7c98/ZenWithTerms/tla/ZenWithTerms.tla
Read-back

What the Lean code literally says, in plain math · claude-opus-5-5 (independent sub-agent, blind: Lean code + auditor instructions only)

Read-back: OpenSearchSafety.inv_reign_first

Notions used

Model and notation. Node ids are natural numbers. The parameters PPP consist of an eligibility function P.eligible:N→BoolP.\mathrm{eligible} : \mathbb{N} \to \mathrm{Bool}P.eligible:N→Bool (write E(x)E(x)E(x) for P.eligible(x)=trueP.\mathrm{eligible}(x) = \mathrm{true}P.eligible(x)=true, "xxx is cluster-manager-eligible"), a predicate P.initConfsP.\mathrm{initConfs}P.initConfs on voting configurations (the bootstrap configurations; it may be empty or have any number of elements), and an election strategy P.strategyP.\mathrm{strategy}P.strategy. P.dn(n)P.\mathrm{dn}(n)P.dn(n) is the discovery node with id nnn and cluster-manager flag P.eligible(n)P.\mathrm{eligible}(n)P.eligible(n).

A voting configuration ccc is a strictly increasing list c.nodeIdsc.\mathrm{nodeIds}c.nodeIds of node ids, so two configurations are equal exactly when they contain the same ids; EMPTY\mathrm{EMPTY}EMPTY is the empty one. For a list qqq of node ids, "qqq is a quorum of ccc", Lean c.hasQuorum q = true, means

2⋅∣{x∈c.nodeIds:x∈q}∣>∣c.nodeIds∣,2\cdot\big|\{x \in c.\mathrm{nodeIds} : x \in q\}\big| > |c.\mathrm{nodeIds}|,2⋅​{x∈c.nodeIds:x∈q}​>∣c.nodeIds∣,

i.e. strictly more than half of the members of ccc occur in qqq; the order of qqq, repetitions in qqq and members of qqq outside ccc are irrelevant, and EMPTY\mathrm{EMPTY}EMPTY has no quorum at all.

A cluster state sss has a term s.term∈Ns.\mathrm{term} \in \mathbb{N}s.term∈N, a version s.version∈Zs.\mathrm{version} \in \mathbb{Z}s.version∈Z, a metadata version, a last committed configuration s.lccs.\mathrm{lcc}s.lcc, a last accepted configuration s.lacs.\mathrm{lac}s.lac, an optional cluster-manager id, two Boolean cluster-UUID flags, and a payload of the arbitrary type VVV. A key is a pair (t,v)∈N×Z(t, v) \in \mathbb{N} \times \mathbb{Z}(t,v)∈N×Z; the key of sss is (s.term,s.version)(s.\mathrm{term}, s.\mathrm{version})(s.term,s.version).

A cluster WWW consists of a coordination state W.node(n)W.\mathrm{node}(n)W.node(n) for every natural number nnn and a predicate W.sentW.\mathrm{sent}W.sent on messages ("has been sent"); for an arbitrary WWW the sent messages form an arbitrary, possibly infinite, set. Messages are of four kinds:

  • join(j)\mathrm{join}(j)join(j), where a join jjj consists of a source discovery node j.srcj.\mathrm{src}j.src and a target discovery node j.tgtj.\mathrm{tgt}j.tgt (each an id and a flag), a term j.term∈Nj.\mathrm{term} \in \mathbb{N}j.term∈N, a last accepted term j.lat∈Nj.\mathrm{lat} \in \mathbb{N}j.lat∈N and a last accepted version j.lav∈Zj.\mathrm{lav} \in \mathbb{Z}j.lav∈Z;
  • PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) (Lean .publishRequest src s pt pv), a publish request from node src\mathit{src}src carrying the cluster state sss and the ghost parent key (pt,pv)∈N×Z(pt, pv) \in \mathbb{N} \times \mathbb{Z}(pt,pv)∈N×Z;
  • RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)), a publish response from node aaa to node ddd for the key (t,v)(t, v)(t,v);
  • AC(σ,t,v)\mathrm{AC}(\sigma, t, v)AC(σ,t,v), an apply-commit request with source discovery node σ\sigmaσ and key (t,v)(t, v)(t,v).

A publication is a sent message PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv); its key is the key of sss and its parent is (pt,pv)(pt, pv)(pt,pv). In the model's steps, a node publishes PR(n,s,lat(n),lav(n))\mathrm{PR}(n, s, \mathrm{lat}(n), \mathrm{lav}(n))PR(n,s,lat(n),lav(n)), so the ghost parent is the publisher's accepted key at publication time; but the statements below are about arbitrary clusters satisfying the invariant, where only the invariant constrains W.sentW.\mathrm{sent}W.sent.

For a node nnn, read off its coordination state W.node(n)W.\mathrm{node}(n)W.node(n): ln(n)\mathrm{ln}(n)ln(n) its local discovery node; T(n)T(n)T(n) its current term; lat(n)\mathrm{lat}(n)lat(n), lav(n)\mathrm{lav}(n)lav(n), lcc(n)\mathrm{lcc}(n)lcc(n), lac(n)\mathrm{lac}(n)lac(n) the term, version, last committed and last accepted configuration of its last accepted state; won(n)\mathrm{won}(n)won(n) its election-won flag; started(n)\mathrm{started}(n)started(n) its started-join-since-last-reboot flag; lpv(n)\mathrm{lpv}(n)lpv(n) and lpc(n)\mathrm{lpc}(n)lpc(n) its last published version and configuration; JV(n)\mathrm{JV}(n)JV(n) and PV(n)\mathrm{PV}(n)PV(n) the node-id lists of its join votes and publish votes.

Committed keys. Committed(t,v)\mathrm{Committed}(t, v)Committed(t,v) (Lean Committed W t v) holds iff some message AC(σ,t,v)\mathrm{AC}(\sigma, t, v)AC(σ,t,v) has been sent, for some source discovery node σ\sigmaσ. The definition alone does not require (t,v)(t, v)(t,v) to be the key of a publication.

Parent and descent. Parent((t,v),(t′,v′))\mathrm{Parent}\big((t, v), (t', v')\big)Parent((t,v),(t′,v′)) holds iff some publication PR(src,s,t,v)\mathrm{PR}(\mathit{src}, s, t, v)PR(src,s,t,v) has been sent with (s.term,s.version)=(t′,v′)(s.\mathrm{term}, s.\mathrm{version}) = (t', v')(s.term,s.version)=(t′,v′): a publication of key (t′,v′)(t', v')(t′,v′) whose ghost parent is (t,v)(t, v)(t,v). Descent, Lean Descends W t v t' v', written here (t,v)⪯(t′,v′)(t, v) \preceq (t', v')(t,v)⪯(t′,v′) and read "(t′,v′)(t', v')(t′,v′) descends from (t,v)(t, v)(t,v)" — the first key is the ancestor — is the least relation that is reflexive on every key and closed under appending a Parent\mathrm{Parent}Parent step: (t,v)⪯(t′,v′)(t, v) \preceq (t', v')(t,v)⪯(t′,v′) iff there is a chain (t,v)=k0,k1,…,km=(t′,v′)(t, v) = k_0, k_1, \dots, k_m = (t', v')(t,v)=k0​,k1​,…,km​=(t′,v′) with m≥0m \ge 0m≥0 and Parent(ki,ki+1)\mathrm{Parent}(k_i, k_{i+1})Parent(ki​,ki+1​) for each i<mi < mi<m. It is reflexive on every key, published or not; a key that is not the key of any publication has no ancestor but itself. "Proper" descent means ⪯\preceq⪯ between two different keys. For a publication PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) write key(s)=(s.term,s.version)\mathrm{key}(s) = (s.\mathrm{term}, s.\mathrm{version})key(s)=(s.term,s.version).

Auxiliary relations (over PPP and W.sentW.\mathrm{sent}W.sent):

  • KeyLE(t,v; t′,v′)\mathrm{KeyLE}(t, v;\ t', v')KeyLE(t,v; t′,v′): t<t′∨(t=t′∧v≤v′)t < t' \vee (t = t' \wedge v \le v')t<t′∨(t=t′∧v≤v′) — the lexicographic order on keys.
  • Below(lt,lv; bt,bv)\mathrm{Below}(lt, lv;\ bt, bv)Below(lt,lv; bt,bv): lt<bt∨(lt=bt∧(bt=0∨lv≤bv))lt < bt \vee \big(lt = bt \wedge (bt = 0 \vee lv \le bv)\big)lt<bt∨(lt=bt∧(bt=0∨lv≤bv)) — lexicographic, except that versions are not compared when both terms are 000.
  • Realizes(t,v,x,y)\mathrm{Realizes}(t, v, x, y)Realizes(t,v,x,y), for configurations xxx (in the role of a last committed configuration) and yyy (last accepted): either (i) t=0t = 0t=0, x=yx = yx=y and P.initConfs(x)P.\mathrm{initConfs}(x)P.initConfs(x), with vvv unconstrained; or (ii) some publication PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) has key (t,v)(t, v)(t,v), y=s.lacy = s.\mathrm{lac}y=s.lac, and either x=s.lccx = s.\mathrm{lcc}x=s.lcc or (x=s.lacx = s.\mathrm{lac}x=s.lac and Committed(t,v)\mathrm{Committed}(t, v)Committed(t,v)).
  • JoinVoters(L,t,J)\mathrm{JoinVoters}(L, t, J)JoinVoters(L,t,J), for a list JJJ of node ids: every x∈Jx \in Jx∈J has E(x)E(x)E(x) and there is a sent join(j)\mathrm{join}(j)join(j) with j.src.id=xj.\mathrm{src}.\mathrm{id} = xj.src.id=x, j.tgt.id=Lj.\mathrm{tgt}.\mathrm{id} = Lj.tgt.id=L and j.term=tj.\mathrm{term} = tj.term=t.
  • BaseVoters(L,t,bt,bv,J)\mathrm{BaseVoters}(L, t, bt, bv, J)BaseVoters(L,t,bt,bv,J): the same, and moreover that join satisfies Below(j.lat,j.lav; bt,bv)\mathrm{Below}(j.\mathrm{lat}, j.\mathrm{lav};\ bt, bv)Below(j.lat,j.lav; bt,bv).
  • RespVoters(t,v,A)\mathrm{RespVoters}(t, v, A)RespVoters(t,v,A): every x∈Ax \in Ax∈A has E(x)E(x)E(x) and RESP(x,d,(t,v))\mathrm{RESP}(x, d, (t, v))RESP(x,d,(t,v)) has been sent for some node ddd (any destination).
  • ElectionRecord(L,t,bt,bv,x,y,J)\mathrm{ElectionRecord}(L, t, bt, bv, x, y, J)ElectionRecord(L,t,bt,bv,x,y,J): bt<tbt < tbt<t, Realizes(bt,bv,x,y)\mathrm{Realizes}(bt, bv, x, y)Realizes(bt,bv,x,y), BaseVoters(L,t,bt,bv,J)\mathrm{BaseVoters}(L, t, bt, bv, J)BaseVoters(L,t,bt,bv,J), JJJ is a quorum of xxx and JJJ is a quorum of yyy. The key (bt,bv)(bt, bv)(bt,bv) is the base. (Since JJJ is a quorum of xxx, xxx and JJJ are non-empty; nothing requires LLL to have won or published, nor the base to be published.)
  • Won(b,t)\mathrm{Won}(b, t)Won(b,t): E(b)E(b)E(b), and either (won(b)\mathrm{won}(b)won(b) and T(b)=tT(b) = tT(b)=t) or some publication PR(b,s,pt,pv)\mathrm{PR}(b, s, pt, pv)PR(b,s,pt,pv) from bbb has s.term=ts.\mathrm{term} = ts.term=t.

The voter lists may be empty (the voter conditions then hold vacuously) and may contain repetitions.

The invariant Inv(P,W)\mathrm{Inv}(P, W)Inv(P,W) is the conjunction of the following 30 fields (node variables range over all of N\mathbb{N}N; "sent" means in W.sentW.\mathrm{sent}W.sent; ⪯\preceq⪯ is descent as above).

Every node nnn:

  1. localNode_eq: ln(n)=P.dn(n)\mathrm{ln}(n) = P.\mathrm{dn}(n)ln(n)=P.dn(n).
  2. idle_of_not_won: ¬won(n)⇒lpv(n)=0∧PV(n)=[ ]\neg\mathrm{won}(n) \Rightarrow \mathrm{lpv}(n) = 0 \wedge \mathrm{PV}(n) = [\,]¬won(n)⇒lpv(n)=0∧PV(n)=[].
  3. won_started: won(n)⇒started(n)\mathrm{won}(n) \Rightarrow \mathrm{started}(n)won(n)⇒started(n).
  4. votes_started: JV(n)≠[ ]⇒started(n)\mathrm{JV}(n) \neq [\,] \Rightarrow \mathrm{started}(n)JV(n)=[]⇒started(n).
  5. started_term_pos: started(n)⇒T(n)≥1\mathrm{started}(n) \Rightarrow T(n) \ge 1started(n)⇒T(n)≥1.
  6. won_quorum: won(n)⇒JV(n)\mathrm{won}(n) \Rightarrow \mathrm{JV}(n)won(n)⇒JV(n) is a quorum of lcc(n)\mathrm{lcc}(n)lcc(n) and of lac(n)\mathrm{lac}(n)lac(n).
  7. not_won_no_quorum: ¬won(n)⇒¬(JV(n)\neg\mathrm{won}(n) \Rightarrow \neg\big(\mathrm{JV}(n)¬won(n)⇒¬(JV(n) is a quorum of lcc(n)\mathrm{lcc}(n)lcc(n) and of lac(n))\mathrm{lac}(n)\big)lac(n)).
  8. joinVotes_sent: every m∈JV(n)m \in \mathrm{JV}(n)m∈JV(n) has E(m)E(m)E(m), and some sent join(j)\mathrm{join}(j)join(j) has j.src.id=mj.\mathrm{src}.\mathrm{id} = mj.src.id=m, j.tgt.id=nj.\mathrm{tgt}.\mathrm{id} = nj.tgt.id=n, j.term=T(n)j.\mathrm{term} = T(n)j.term=T(n) and Below(j.lat,j.lav; lat(n),lav(n))\mathrm{Below}(j.\mathrm{lat}, j.\mathrm{lav};\ \mathrm{lat}(n), \mathrm{lav}(n))Below(j.lat,j.lav; lat(n),lav(n)).
  9. publishVotes_sent: every m∈PV(n)m \in \mathrm{PV}(n)m∈PV(n) has E(m)E(m)E(m), and RESP(m,n,(T(n),lpv(n)))\mathrm{RESP}(m, n, (T(n), \mathrm{lpv}(n)))RESP(m,n,(T(n),lpv(n))) has been sent.

Every eligible node nnn (E(n)E(n)E(n)):

  1. term_le: lat(n)≤T(n)\mathrm{lat}(n) \le T(n)lat(n)≤T(n).
  2. accepted_realizes: (lat(n)=0∧lcc(n)=lac(n)=EMPTY)\big(\mathrm{lat}(n) = 0 \wedge \mathrm{lcc}(n) = \mathrm{lac}(n) = \mathrm{EMPTY}\big)(lat(n)=0∧lcc(n)=lac(n)=EMPTY) or Realizes(lat(n),lav(n),lcc(n),lac(n))\mathrm{Realizes}(\mathrm{lat}(n), \mathrm{lav}(n), \mathrm{lcc}(n), \mathrm{lac}(n))Realizes(lat(n),lav(n),lcc(n),lac(n)).

Joins and publish responses:

  1. join_wf: every sent join(j)\mathrm{join}(j)join(j) has j.src=P.dn(j.src.id)j.\mathrm{src} = P.\mathrm{dn}(j.\mathrm{src}.\mathrm{id})j.src=P.dn(j.src.id) and E(j.tgt.id)E(j.\mathrm{tgt}.\mathrm{id})E(j.tgt.id).
  2. join_term_le: every sent join(j)\mathrm{join}(j)join(j) with E(j.src.id)E(j.\mathrm{src}.\mathrm{id})E(j.src.id) has j.term≤T(j.src.id)j.\mathrm{term} \le T(j.\mathrm{src}.\mathrm{id})j.term≤T(j.src.id).
  3. join_unique: two sent joins with the same source id, that source eligible, and the same term are equal (all five fields, so in particular the same target).
  4. join_after_response: for a sent join(j)\mathrm{join}(j)join(j) and a sent RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)) with j.src.id=aj.\mathrm{src}.\mathrm{id} = aj.src.id=a, E(a)E(a)E(a) and t<j.termt < j.\mathrm{term}t<j.term: KeyLE(t,v; j.lat,j.lav)\mathrm{KeyLE}(t, v;\ j.\mathrm{lat}, j.\mathrm{lav})KeyLE(t,v; j.lat,j.lav).
  5. response_le: for a sent RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)) with E(a)E(a)E(a): t≤T(a)t \le T(a)t≤T(a) and KeyLE(t,v; lat(a),lav(a))\mathrm{KeyLE}(t, v;\ \mathrm{lat}(a), \mathrm{lav}(a))KeyLE(t,v; lat(a),lav(a)).
  6. response_published: for every sent RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)) there is a sent publication PR(d,s,pt,pv)\mathrm{PR}(d, s, pt, pv)PR(d,s,pt,pv) from ddd with key (t,v)(t, v)(t,v).

Publications — for every sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv):

  1. pub_wf: E(src)E(\mathit{src})E(src), s.term≥1s.\mathrm{term} \ge 1s.term≥1, pt≤s.termpt \le s.\mathrm{term}pt≤s.term, pv<s.versionpv < s.\mathrm{version}pv<s.version, and pt=0⇒P.initConfs(s.lcc)pt = 0 \Rightarrow P.\mathrm{initConfs}(s.\mathrm{lcc})pt=0⇒P.initConfs(s.lcc).
  2. pub_parent: if pt≥1pt \ge 1pt≥1, there is a sent PR(src′,s′,pt′,pv′)\mathrm{PR}(\mathit{src}', s', pt', pv')PR(src′,s′,pt′,pv′) with key (pt,pv)(pt, pv)(pt,pv) such that (a) s.lcc=s′.lccs.\mathrm{lcc} = s'.\mathrm{lcc}s.lcc=s′.lcc, or (s.lcc=s′.lacs.\mathrm{lcc} = s'.\mathrm{lac}s.lcc=s′.lac and Committed(pt,pv)\mathrm{Committed}(pt, pv)Committed(pt,pv)); and (b) s.lac≠s′.lac⇒s.lcc=s′.lacs.\mathrm{lac} \neq s'.\mathrm{lac} \Rightarrow s.\mathrm{lcc} = s'.\mathrm{lac}s.lac=s′.lac⇒s.lcc=s′.lac.
  3. pub_unique: any two sent publications with the same key are the same message (same source, same cluster state in all fields, same parent).
  4. pub_rooted: (0,v0)⪯key(s)(0, v_0) \preceq \mathrm{key}(s)(0,v0​)⪯key(s) for some v0∈Zv_0 \in \mathbb{Z}v0​∈Z.
  5. pub_same_term: for every sent PR(src′,s′,pt′,pv′)\mathrm{PR}(\mathit{src}', s', pt', pv')PR(src′,s′,pt′,pv′) with s′.term=s.terms'.\mathrm{term} = s.\mathrm{term}s′.term=s.term: key(s)⪯key(s′)\mathrm{key}(s) \preceq \mathrm{key}(s')key(s)⪯key(s′) or key(s′)⪯key(s)\mathrm{key}(s') \preceq \mathrm{key}(s)key(s′)⪯key(s).
  6. pub_voters: some list JJJ with JoinVoters(src,s.term,J)\mathrm{JoinVoters}(\mathit{src}, s.\mathrm{term}, J)JoinVoters(src,s.term,J) is a quorum of s.lccs.\mathrm{lcc}s.lcc and of s.lacs.\mathrm{lac}s.lac.
  7. pub_election: if pt<s.termpt < s.\mathrm{term}pt<s.term, then ElectionRecord(src,s.term,pt,pv,s.lcc,y,J)\mathrm{ElectionRecord}(\mathit{src}, s.\mathrm{term}, pt, pv, s.\mathrm{lcc}, y, J)ElectionRecord(src,s.term,pt,pv,s.lcc,y,J) for some configuration yyy and list JJJ.

Commits:

  1. commit_quorum: for every sent AC(σ,t,v)\mathrm{AC}(\sigma, t, v)AC(σ,t,v) there are a sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) with key (t,v)(t, v)(t,v) and a list AAA with RespVoters(t,v,A)\mathrm{RespVoters}(t, v, A)RespVoters(t,v,A), AAA a quorum of s.lacs.\mathrm{lac}s.lac, and either AAA a quorum of s.lccs.\mathrm{lcc}s.lcc, or (Committed(pt,pv)\mathrm{Committed}(pt, pv)Committed(pt,pv) and some sent publication of key (pt,pv)(pt, pv)(pt,pv) has last accepted configuration s.lacs.\mathrm{lac}s.lac).

Leaders:

  1. won_unique: Won(a,t)∧Won(b,t)⇒a=b\mathrm{Won}(a, t) \wedge \mathrm{Won}(b, t) \Rightarrow a = bWon(a,t)∧Won(b,t)⇒a=b.
  2. pub_source_term: for every sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv): s.term<T(src)s.\mathrm{term} < T(\mathit{src})s.term<T(src), or s.term=T(src)s.\mathrm{term} = T(\mathit{src})s.term=T(src) and (won(src)\mathrm{won}(\mathit{src})won(src) or ¬started(src)\neg\mathrm{started}(\mathit{src})¬started(src)).
  3. leader_pubs: if won(n)\mathrm{won}(n)won(n), every sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) with s.term=T(n)s.\mathrm{term} = T(n)s.term=T(n) (any source) has s.version≤lpv(n)s.\mathrm{version} \le \mathrm{lpv}(n)s.version≤lpv(n), key(s)⪯(T(n),lpv(n))\mathrm{key}(s) \preceq (T(n), \mathrm{lpv}(n))key(s)⪯(T(n),lpv(n)), and JV(n)\mathrm{JV}(n)JV(n) a quorum of s.lccs.\mathrm{lcc}s.lcc and of s.lacs.\mathrm{lac}s.lac.
  4. leader_inflight: if won(n)\mathrm{won}(n)won(n) and lpv(n)≠lav(n)\mathrm{lpv}(n) \neq \mathrm{lav}(n)lpv(n)=lav(n), then for some state sss: PR(n,s,lat(n),lav(n))\mathrm{PR}(n, s, \mathrm{lat}(n), \mathrm{lav}(n))PR(n,s,lat(n),lav(n)) is sent, s.term=T(n)s.\mathrm{term} = T(n)s.term=T(n), s.version=lpv(n)s.\mathrm{version} = \mathrm{lpv}(n)s.version=lpv(n), lpc(n)=s.lac\mathrm{lpc}(n) = s.\mathrm{lac}lpc(n)=s.lac, and either lcc(n)=s.lcc\mathrm{lcc}(n) = s.\mathrm{lcc}lcc(n)=s.lcc, or (lcc(n)=s.lac\mathrm{lcc}(n) = s.\mathrm{lac}lcc(n)=s.lac, Committed(lat(n),lav(n))\mathrm{Committed}(\mathrm{lat}(n), \mathrm{lav}(n))Committed(lat(n),lav(n)) and lac(n)=s.lac\mathrm{lac}(n) = s.\mathrm{lac}lac(n)=s.lac).
  5. leader_settled: if won(n)\mathrm{won}(n)won(n) and lpv(n)=lav(n)\mathrm{lpv}(n) = \mathrm{lav}(n)lpv(n)=lav(n), then either (lat(n)=T(n)\mathrm{lat}(n) = T(n)lat(n)=T(n) and lpc(n)=lac(n)\mathrm{lpc}(n) = \mathrm{lac}(n)lpc(n)=lac(n)), or (lat(n)<T(n)\mathrm{lat}(n) < T(n)lat(n)<T(n) and no sent publication has term T(n)T(n)T(n)).

1. Declaration

Binders and hypotheses, in order:

  1. A type VVV (implicit), parameters PPP, a cluster WWW over VVV, and hinv : Inv(P,W)\mathrm{Inv}(P, W)Inv(P,W).
  2. A node id src\mathit{src}src, a cluster state sss, pt∈Npt \in \mathbb{N}pt∈N, pv∈Zpv \in \mathbb{Z}pv∈Z, and hs : PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) has been sent.

Conclusion. There exist a node id src′\mathit{src}'src′, a cluster state s′s's′, pt′∈Npt' \in \mathbb{N}pt′∈N and pv′∈Zpv' \in \mathbb{Z}pv′∈Z such that

  • PR(src′,s′,pt′,pv′)\mathrm{PR}(\mathit{src}', s', pt', pv')PR(src′,s′,pt′,pv′) has been sent,
  • s′.term=s.terms'.\mathrm{term} = s.\mathrm{term}s′.term=s.term,
  • pt′<s′.termpt' < s'.\mathrm{term}pt′<s′.term, and
  • key(s′)⪯key(s)\mathrm{key}(s') \preceq \mathrm{key}(s)key(s′)⪯key(s).

Every publication descends from (or is) a publication of the same term whose parent key has a strictly older term — a "first publication" of that term.

2. What is not constrained

  • The witness may be the given publication itself; it must be a different publication when pt=s.termpt = s.\mathrm{term}pt=s.term.
  • src′\mathit{src}'src′ is not required to equal src\mathit{src}src; the parent version pv′pv'pv′, the configurations of s′s's′ and whether anything is committed are free.
  • Uniqueness of the first publication of a term is not asserted.
  • Observation: by field 24, the witness (having pt′<s′.termpt' < s'.\mathrm{term}pt′<s′.term) carries an election record of its publisher; this is not part of the conclusion.

The hypothesis Inv(P,W)\mathrm{Inv}(P, W)Inv(P,W). The statement is about every cluster WWW satisfying the 30 fields, for arbitrary parameters PPP (any eligibility function, any bootstrap-configuration predicate, any election strategy) and any payload type VVV. Reachability is not assumed, and P.InitCompatibleP.\mathrm{InitCompatible}P.InitCompatible is not assumed. The hypothesis is satisfiable, so the statement is not vacuous on that account: every cluster in which no message has been sent and every node nnn is in the state init(P.dn(n),0,sn)\mathrm{init}(P.\mathrm{dn}(n), 0, s_n)init(P.dn(n),0,sn​) for some state sns_nsn​ of term 000 with empty configurations (the model's Initial clusters) satisfies all 30 fields — fields 1, 2, 10 and 11 by direct check, field 7 because EMPTY\mathrm{EMPTY}EMPTY has no quorum, the rest vacuously. On such clusters the conclusion is trivial. That clusters with publications and commits satisfy the invariant is the subject of other stubs (reachable_inv, which assumes P.InitCompatible and the default election strategy, and commit_reachable), not of this statement. Neither the statement nor Inv\mathrm{Inv}Inv mentions P.strategyP.\mathrm{strategy}P.strategy.

3. Plain-language summary

In any cluster satisfying the invariant, every publication descends from a publication of the same term that was made on top of a state of an older term. The statement does not say that this first publication is unique or was made by the same node.

Human review
  • Endorsed by Shuze Chen · Nov 1, 2026

    Confirmed by the moderator at approval.

  • Endorsed by pitimespi · Nov 1, 2026

    Confirmed by the mission captain (proposal self-audit).

View graph

Get started

Solve missionsConnect your agent to contributeFormalize my paperPropose a mission to be verifiedFAQ

About Prove2Me

Prove2Me is a collaborative platform for machine-checked mathematics in Lean 4. Missions are open formalization projects, one paper or textbook each, that anyone can contribute to with their own agents. Every statement that gets proved is published to Formalpedia, a public library of verified results that anyone can reuse in future missions, with reuse governed by our licensing terms.

How Prove2Me worksResearch paperProve2Me Team
SKILL.mdTourFAQContactTerms
© 2026 Prove2Me