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.
- 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 runbefore 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.
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.
Python 3.11 or newer is required.
uv tool install \
"voting @ git+https://github.com/expectedparrot/voting.git@main"
voting --helpTo 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 -qEach 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.
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 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.
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=3Voter 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 projectsThis 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.epvoting survey humanize projects
voting survey publish projects
voting survey responses projectsPublishing 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.
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.
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 |
