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 35 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.
One Town, Many Ways to Choose is a LaTeX manual with an extended fictional town example, mathematical definitions, algorithm explanations, exercises, and actual CLI commands. Its build executes the local examples and generates the result tables from their output.
voting docs manual --output-dir town-manual --pdfChoose a new output directory. Omit --pdf to export the example and LaTeX
without TeX installed; use --sources-only to export just the sources.
No EDSL, credentials, or model calls are needed for the worked examples.
PDF compilation requires pdflatex with Latin Modern, PGFPlots, and xurl.
See voting docs show manual for build artifacts and authoring instructions.
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.
Use these flags on election add, or update an existing election:
voting election configure budget_vote --budget 100
voting election configure committee --seats 3 --approval-limit 2
voting election configure reviews --grade reject --grade fair --grade good --grade excellentGrades are ordered from worst to best. --clear-budget and
--clear-approval-limit remove optional limits. Voter weights must be finite
and positive; allocations must be finite and nonnegative. Direct entry,
imports, and counting share ballot validation. Invalid import rows are
reported and skipped. To approve nothing, use ballot approve ... --abstain
or import "approved": [].
Only the lexicographic tie policy is supported. Counts reject incompatible
ballot types and seat counts. Default count compare reports skipped methods
in methods_skipped, including Kemeny–Young above nine eligible options; use
--allow-expensive to opt into its factorial search. Explicit incompatible
method requests fail before any comparison results are saved.
status and next track each election separately and identify stale counts
when ballots, eligibility, or counting settings change. Saved counts include
ballot IDs, eligible option IDs, package version, and an input fingerprint.
Older results without a fingerprint should be recounted.
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 manual |
Build the LaTeX textbook and execute its local worked examples. |
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 configure |
Set seats, budget, grade scale, and approval limits. |
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 |
