wedgeGate
The RaftMetric.WedgeSuspected.Gate a refusal at this gate is reported under, or null when this gate feeds no wedge diagnosis at all.
The one place the two vocabularies meet, and the reason they stay two types. RaftMetric.WedgeSuspected is a diagnosis of a sustained condition, raised only by the two gates that sit at RaftEngine.onMessage's dispatch boundary. Widening its Gate to the fifteen values here would put values in the metric's type that the metric can never carry — a worse lie than the duplication it would avoid. Expressing the relation here instead makes WedgeSuspected.Gate a subset the compiler checks: this when has no else, so a new entry above cannot compile without saying which side of it it is on.
null is the honest answer for every gate past the dispatch boundary — the five TimeoutNow* guards and the seven handler-lane guards alike. They run downstream of the two gates the wedge report is built from; a refusal there says nothing about this node being unable to make progress, which is the only thing that report means.
For the handler-lane gates that is not only a policy call, it is forced by the mechanism, and the check is worth recording because the opposite reading looks plausible: a node that pinned a forger and now drops the honest leader's every frame at ForgedLeaderForTerm really is jammed. It still cannot be reported here. RaftEngine.onMessage zeroes refusedLeaderFrameRun for any leader→peer frame at or above our term that clears both dispatch gates — and every frame that reaches a handler has cleared both by definition. So the run is reset immediately before each of these gates sees the frame, and noteRefusedLeaderFrame could never accumulate the WEDGE_SUSPECTED_RUN consecutive refusals a report requires. Naming a wedge gate here would declare a report that structurally cannot fire.
ImplausibleNegativeTerm maps to RaftMetric.WedgeSuspected.Gate.TermJump because that is where it is reported today — it shared an if with ImplausibleTermJump before #1989 split them. Note this is a naming, not a prediction that the metric fires: whether a given frame actually produces a report is RaftEngine.noteRefusedLeaderFrame's call, made on the frame's term, and a negative term is always below ours so it is always excluded there. That filter is a per-frame runtime fact and belongs at the one site that owns it, not pre-empted statically here.