Skip to content

Feature request: "simple majority of cast votes" pass mode (more +1 than -1, excluding abstentions and non-voters) #707

Closed

Description

@hendrikebbers

Add a pass mode where a vote passes when there are more binding votes in favor (+1) than against (-1), and where abstentions (eyes) and maintainers who did not vote are not counted at all.

Today the pass decision is a percentage of all allowed voters and never looks at the against count. Our governance requires the opposite: a plurality of the votes actually cast, ignoring everyone who abstained or stayed silent. We currently cannot express this with pass_threshold.

Current behavior

From src/results.rs, the result is computed as:

in_favor_percentage = in_favor / allowed_voters.len() * 100
passed = in_favor_percentage >= pass_threshold

This means:

  1. The denominator is always the full set of allowed_voters. Abstentions (eyes) and non-voters stay in the denominator and drag the percentage down — there is no way to exclude them.
  2. The against count is tracked (against_percentage) but never used in the passed decision. There is no "more in favor than against" comparison.
    So a vote with 2 in favor, 0 against, 10 abstentions in a 12-maintainer team yields 2 / 12 = 16.7 %, which fails at any pass_threshold >= 17. Lowering pass_threshold doesn't help either, because a low threshold would also pass a vote with more -1 than +1 (e.g. 2 in favor, 10 against), since against is ignored.

Use case

The Hiero project (LF Decentralized Trust) uses GitVote for all asynchronous maintainer votes. Our governance defines the passing rule as follows (see roles/roles-and-groups.md, "Voting" section):

A vote passes when more maintainers have voted in favor (+1 / thumbs up in GitVote) than against (-1 / thumbs down in GitVote). Maintainers who abstain or do not vote are not counted. For example, 2 votes in favor and 0 against with 10 abstentions is a passing vote.

Abstaining is an explicit, first-class outcome in our process: maintainers are required to participate in a nomination but may effectively abstain by only posting a comment. Non-participation is a separate governance concern (inactivity), not a "no" vote. So neither abstentions nor non-voters may count toward or against the result.

We suspect this "plurality of participants" model is common in maintainer/promotion votes and would be useful beyond our project.

Proposed solution

Introduce an alternative pass mode, selectable per profile. For example:

profiles:
  default:
    duration: 5m
    # Instead of pass_threshold, allow a mode that compares cast votes:
    pass_mode: simple_majority   # passes when in_favor > against; abstain & not_voted ignored
    allowed_voters:
      teams:
        - project-maintainers

Behavior of simple_majority:

  • passed = in_favor > against (binding votes only).
  • Abstentions and non-voters are excluded from the decision entirely.
  • Optionally require at least one +1 (i.e. an empty vote with 0 in favor and 0 against does not pass).
    Naming is of course up to you (pass_mode, or a quorum/majority sub-section). The key point is a mode that (a) compares in_favor against against, and (b) uses cast votes rather than the full allowed_voters set as the basis.

Alternatives considered

  • Low pass_threshold: rejected — it ignores against and would pass votes with more -1 than +1.
  • Threshold as a % of all maintainers: changes the semantics (a fixed quorum of the whole team), and depends on team size; it does not match "more for than against, ignore abstentions".
  • Custom GitHub Action counting reactions: works, but we'd rather keep using GitVote as the single, consistent voting tool across all Hiero projects.

Additional context

This would also make the existing "close on passing" / early-close logic more expressive for us, since our governance already allows closing a vote early once the outcome can no longer change (enough +1 that remaining votes can't flip it, or enough -1 that +1 > -1 is impossible).

Happy to help implement, test or review a PR for this issue.

Activity

  1. tegioz commented on Jul 16, 2026

    @tegioz
    Contributor

    Thanks @hendrikebbers, we'll look into it 👍

    Related to #648

  2. hendrikebbers commented on Sep 23, 2026

    @hendrikebbers
    Author

    @tegioz we (https://github.com/hiero-ledger) would be more than happy to help to get the feature in. If we know that it will be not an option for you we need to fork the tool.

  3. tegioz commented on Sep 23, 2026

    @tegioz
    Contributor

    Hi @hendrikebbers

    We've plans to work on this soon (hopefully next week if everything goes well), we'll keep you posted 👍

    If you prefer to fork it, that sounds great too, whatever works best for you 🙂

  4. hendrikebbers commented on Sep 24, 2026

    @hendrikebbers
    Author

    @tegioz that is awesome :) Thank you so much. If you need any help or additional input (or a beta tester) just ping me

  5. tegioz commented on Sep 24, 2026

    @tegioz
    Contributor

    Will do, thanks!

  6. tegioz commented on Sep 29, 2026

    @tegioz
    Contributor

    Closed by #709

    You can find more details about how it works in the reference config file. It'd be great if you could give it a try when you have a chance @hendrikebbers 🙂

    # Pass threshold base (optional)
    #
    # Number the in favor and against percentages are calculated over, which is
    # also used to decide if the vote passes. Only binding votes are counted.
    #
    # - allowed_voters (default): all users with binding votes, so users who
    # do not vote count towards the percentage calculation too.
    #
    # - votes_cast: binding votes cast (in favor, against and abstain).
    # Abstentions are included in the percentage calculation by default.
    # When `exclude_abstentions` is set to true, only votes in favor and
    # against are included. Abstentions are still displayed and recorded
    # as binding votes, though.
    #
    # If no votes are counted, the vote does not pass. Please note that there is
    # no minimum participation required when using votes_cast, so a low turnout
    # can decide a vote.
    #
    # Each profile sets this option independently (the value set in the default
    # profile is not inherited by other profiles).
    #
    # pass_threshold_base: allowed_voters
    #
    # pass_threshold_base:
    # votes_cast: {}
    #
    # pass_threshold_base:
    # votes_cast:
    # exclude_abstentions: true
    #
    # More votes in favor than against
    #
    # To require more votes in favor than against (ignoring abstentions and users
    # who do not vote), use votes_cast excluding abstentions with a pass
    # threshold of 50.01. A tie does not pass with 50.01, but it would pass with
    # 50. This is exact for up to 2,499 votes against. Comments will display
    # `50.01%` as the passing threshold.
    #
    # pass_threshold: 50.01
    # pass_threshold_base:
    # votes_cast:
    # exclude_abstentions: true
    #
    pass_threshold_base: allowed_voters

  7. hendrikebbers commented on Oct 6, 2026

    @hendrikebbers
    Author

    @tegioz thank you. we will test it out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions