Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

voting — election-method modeling and ballot analysis CLI

Expected Parrot voting mark: a parrot casting a ballot at an electronic voting booth

View the project website

voting is a JSON-first command-line toolkit for preference research and group-decision analysis: define an election (in the social-choice sense — any group choosing among options), collect ballots from people or AI personas, and compare how counting methods treat the same data. It supports single- and multi-winner counts across plurality, ranked, approval, score, grade, allocation, and Condorcet families.

It is a research, teaching, and decision-support tool — not election-administration software.

It can:

  • Register candidates, proposals, voters, weights, and eligibility.
  • Configure elections with a ballot type, number of seats, and tie policy (counting methods are chosen at count time, never fixed on the election).
  • Record ballots directly or import them from another system.
  • Validate ballots before counting.
  • Run 32 method names and aliases — one at a time (count run) or every compatible method at once (count compare) — including FPTP, approval, score, STAR, IRV, STV, Borda, Schulze, ranked pairs, Copeland, Kemeny–Young, Bucklin, runoff, cumulative, quadratic voting, Method of Equal Shares, and majority judgment.
  • Save every count so results from different methods can be inspected and compared.
  • Generate synthetic EDSL preference studies or publish Humanize surveys for real respondents.

What people use it for

  • Roadmap and feature prioritization — replace dot-voting (plurality by accident) with ranked ballots and count compare; if methods disagree, the priority is contested and deserves discussion, not a tally.
  • Hiring and promotion panels — ranked ballots per interviewer, explicit role weights, Condorcet counting, and a pairwise plot of every head-to-head.
  • Customer and employee preference surveys — publish a hosted Humanize ranking survey, email unique links, import the responses as ballots, and read the rank-distribution plot ("broadly liked" vs "polarizing").
  • AI-persona pretesting — pilot the question against synthetic voter personas via survey generate + ep run before spending respondent budget.
  • Budget allocation — "split 100 points across these initiatives" maps to allocated ballots; compare cumulative, quadratic, and Method of Equal Shares to see the utilitarian/proportional trade-off in your own data.

Each of these is a worked six-command recipe in voting docs show recipes.

Use with a coding agent

Copy this into Codex or Claude Code:

Install voting and help me run an election:

uv tool install --upgrade --force \
  "voting[humanize] @ git+https://github.com/expectedparrot/voting.git@main"

Run `voting agent-bootstrap` and follow its instructions and `next_steps`.

agent-bootstrap works before a project exists and at every later phase. It returns the agent's operating rules, the current project state, the getting-started guide, and executable next steps. Run it again after material changes or when resuming work.

Install

Python 3.11 or newer is required.

uv tool install \
  "voting @ git+https://github.com/expectedparrot/voting.git@main"
voting --help

To include EDSL support for synthetic and hosted surveys:

uv tool install --upgrade --force \
  --with-executables-from "edsl @ git+https://github.com/expectedparrot/edsl.git@main" \
  "voting[humanize] @ git+https://github.com/expectedparrot/voting.git@main"

Local ballot entry and counting do not require EDSL or authentication.

For development:

git clone https://github.com/expectedparrot/voting.git
cd voting
uv sync --extra dev --extra humanize
pytest -q

How a voting project works

Each project stores its state under .voting/:

.voting/
├── meta.json
├── options/       candidates or proposals
├── voters/        voters, weights, eligibility, and traits
├── elections/     ballot type, seats, and eligible options
├── ballots/       append-only ballot records
├── results/       saved count runs
└── output/        survey job packages and manifests

The normal lifecycle is:

init → setup options and voters → configure election → collect ballots
     → validate → count → inspect or compare results

voting status reports the inferred phase and recommended next commands. Commands emit structured JSON by default; use the top-level --human flag for terminal-friendly output.

Complete example

This election records three ranked ballots, counts them with IRV, then reuses the same ballots for a Borda comparison:

voting init neighborhood_vote --description "Choose one neighborhood project"
cd neighborhood_vote

voting option add library "Extend library hours" --type proposal
voting option add shelters "Build covered bus shelters" --type proposal
voting option add lighting "Upgrade park lighting" --type proposal

voting voter add voter_1 "Voter 1"
voting voter add voter_2 "Voter 2"
voting voter add voter_3 "Voter 3"

voting election add projects "Neighborhood projects" --ballot-type ranked
voting election add-option projects library
voting election add-option projects shelters
voting election add-option projects lighting
voting election open projects

voting ballot rank projects voter_1 library shelters lighting
voting ballot rank projects voter_2 shelters lighting library
voting ballot rank projects voter_3 lighting library shelters

voting ballot validate projects
voting count run projects --method irv
voting count run projects --method borda
voting count list

Ballot formats

Ballot type What the voter supplies Example methods
single_choice One option FPTP, majority, runoff, SNTV
ranked Options in preference order IRV, STV, Borda, Condorcet, Bucklin
approval Any number of approved options Approval, block, limited voting
score Numeric scores by option Score, STAR
grade Ordered labels such as good or fair Majority judgment
allocated A point budget distributed across options Cumulative, quadratic, equal shares

Ballot type determines what preference information is available. A single-choice ballot cannot recover second preferences, and an approval ballot does not rank the approved options.

Three ways to collect preferences

Record ballots directly

voting ballot cast election_id voter_id --choice option_id
voting ballot rank election_id voter_id first second third
voting ballot approve election_id voter_id --option first --option second
voting ballot score election_id voter_id first=5 second=3 third=0
voting ballot grade election_id voter_id first=excellent second=good
voting ballot allocate election_id voter_id first=7 second=3

Generate synthetic preferences

Voter traits can describe the personas used by an EDSL study:

voting voter set-trait voter_1 persona \
  '"Daily bus rider who uses the library on weekends"'
voting survey generate projects
voting --human survey show projects

This builds .voting/output/survey_projects.jobs.ep — an EDSL Jobs package carrying the survey, one agent per voter, and the model. voting never executes model calls; run the job externally and import the results:

ep run --jobs .voting/output/survey_projects.jobs.ep \
    --output .voting/output/survey_projects.results.ep
voting ballot import --election projects \
    --from-results .voting/output/survey_projects.results.ep

Publish a survey for people

voting survey humanize projects
voting survey publish projects
voting survey responses projects

Publishing returns respondent and admin URLs. Email invitations are also supported through voting survey email; see voting docs show humanize. Authentication is handled by the ep CLI and is only needed for these EDSL workflows.

Output

Every command returns one JSON envelope:

{
  "command": "status",
  "status": "ok",
  "data": {},
  "warnings": [],
  "errors": [],
  "next_steps": []
}

Count results include winners, ranking, ballot totals, method-specific diagnostics, warnings, and the settings used for that run. Results are saved under .voting/results/, making method comparisons reproducible.

Command reference

Every registered command (options and defaults live in voting <command> --help; tests/test_contract_sync.py keeps this table and the CLI from drifting apart).

Command Purpose
voting agent-bootstrap Return the agent contract, current state, guide, and next actions.
voting ballot allocate
voting ballot approve
voting ballot cast
voting ballot grade
voting ballot import Import ballots from a generated-script results file or an EDSL Results object.
voting ballot list
voting ballot rank
voting ballot score
voting ballot show
voting ballot validate
voting capabilities Describe the agent-facing output contract and external-action surface.
voting count compare Count the same ballots under every compatible method in one command.
voting count list
voting count run
voting count show
voting docs list List all available documentation topics.
voting docs search Search documentation by keyword.
voting docs show Show the full text of a documentation topic.
voting election add
voting election add-option
voting election close
voting election list
voting election open
voting election remove-option
voting election show
voting info
voting init
voting next Return the single highest-priority next action from project state.
voting option add
voting option import Import many options from a JSON file, optionally attaching them to an election.
voting option list
voting option set-eligible
voting option show
voting plot methods Grid of finishing positions across every saved count — does the method change the winner?
voting plot pairwise Head-to-head margin matrix from a Condorcet-style count result.
voting plot ranks Stacked bars of where voters ranked each option (latest ballot per voter).
voting plot scores Bar chart of a count's per-option totals, winner highlighted.
voting status Show current project phase, counts, and recommended next steps.
voting survey email Email unique Humanize voting links to configured voters.
voting survey generate Build an EDSL Jobs package (.jobs.ep) that elicits AI voter preferences via ep run.
voting survey humanize Generate a model-free EDSL job for a Humanize voting survey.
voting survey publish Create a hosted Humanize survey through the ep CLI and save its URLs.
voting survey responses Download Humanize responses as an EDSL Results package.
voting survey show Display a generated survey job's manifest for inspection before ep run.
voting version Report the installed build and envelope schema version.
voting voter add
voting voter list
voting voter set-eligible
voting voter set-trait
voting voter show

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages