Repository & Engine Guide¶
How this repo is laid out, how to run it, how the voting methods dispatch, and what the vendored engine adds. Moved here from the landing README to keep the front page short — this is the "how it works / how to run it" reference.
Start with Start Here for the guided tour, and CLAUDE.md — working guidance for this repo for the house conventions.
Repository map¶
| Area | What's there |
|---|---|
STAR engine (LH starvote fork) |
The STAR engine (single-winner STAR, Bloc / proportional STAR, Approval, Ranked Robin), reporting options, the preference matrix, the [Divergence from STAR] comparison, the show_runoff_percent runoff summary (on by default), and the lot_numbers tie-break. Vendored fork of starvote; see Fork Notes — starvote (vendored fork). |
| RCV-IRV engine (pyrankvote) | Vendored RCV-IRV engine. Ranked ballots (A>B>C) route here automatically. Concept pages: RCV-IRV (Hare). |
| pref_voting cross-check engine | Independent cross-check engine — wraps Eric Pacuit's pref_voting to verify the LH engine's Condorcet / IRV / Plurality (and report Copeland = Ranked Robin). Optional dep. |
| ABCvoting engine | Multi-winner Approval / ABC rules (Martin Lackner's abcvoting) — SPAV / PAV / Phragmén and an AV cross-check of the LH bloc-Approval count. Optional dep. |
| YAML library — imports & converter | BetterVoting JSON → YAML converter (01_convert_json_yaml.py) and imported elections (YAML_library/1_positive/). |
| YAML library — negative fixtures | Malformed fixtures (YAML_library/2_negative/) — every one must fail with the right plain-language error (see Validation philosophy below). |
| Example elections (01_STAR … 06_Other) | Hand-authored example elections, grouped by content type: STAR, Bloc STAR, proportional STAR, Approval, Ranked Robin, method comparisons, other methods. |
| Simulations | Monte-Carlo scripts: Favorite Betrayal frequency and runoff-reversal rates. |
| Divergence review | Case index + CSV of elections where methods disagree (STAR vs IRV vs Condorcet…). |
| Demo dropbox (watch-folder) | Watch-folder demo: drop a BetterVoting JSON export in, get canonical YAML + tabulation out. (GitHub link — the folder is staging space, excluded from the website.) |
| YAML test-case index | Auto-generated catalog of every election YAML, grouped by voting method (STARVote_LH_tabulation_engine/tools_adam/scripts/build_yaml_index.py; a pytest keeps it current). Browse cases by method without moving files. |
| BetterVoting test-case registry | Repo-native, sortable index of every BetterVoting-backed case — BV_registry.md + bv_cases.csv: Test ID, method, winners, election id, live /results link, page, YAML. These back the regression cases that reproduce and guard real BetterVoting bugs; new BV elections are created via the API with create_bv_test_election.py. The Google Sheet stays a thin registry (Test-ID assignment); the repo is canonical for tabulation cases. |
Tabulated mirrors (*_tabulated/) |
Generated plain-text tabulation copies (regenerated by re-running the YAMLs). |
Docs hub (07_Concepts/) |
The docs hub — curriculum, concept pages, glossary, terminology, and organization conventions. |
| Conversation scripts | Larry ↔ Adam debate / teaching scripts — index; episodes live beside their topics. |
Test suite (…/tests/) |
The pytest suite + pre-commit hook. |
| Upstream bug reports | Follow-up list for every bug we filed against a project we don't own — BetterVoting, Larry's starvote, GitHub Pages — with state and a one-liner to re-check them. Comparing engines finds bugs; this is where they don't get forgotten. |
Quick start¶
Setup (once) — clone and let uv build the environment from the lockfile; optionally wire up the pre-commit test hook:
git clone https://github.com/masiarek/star-voting-library
cd star-voting-library
uv sync
git config core.hooksPath STARVote_LH_tabulation_engine/tools_adam/scripts/git-hooks
All commands below run from the repo root; uv run uses the project's .venv automatically.
Tabulate an election file — annotated on-screen report on screen, plus a _tabulated.txt sibling:
uv run python STARVote_LH_tabulation_engine/starvote_larry_hastings.py \
01_STAR/your_election.yaml
Import a BetterVoting JSON export — converts every *.json in the folder into canonical YAML under _generated/:
uv run python YAML_library/1_positive/01_convert_json_yaml.py
Run the test suite (same suite CI runs on every push):
uv run pytest
Python version — why .python-version matters (a uv gotcha)¶
Two files work together to pin the interpreter, and they do different jobs:
pyproject.toml→requires-python = ">=3.10,<3.14"is a constraint — uv checks it and refuses to run on anything outside the range. The<3.14cap is deliberate:pref_voting/numbadon't support Python 3.14 yet..python-version(=3.13) is the selector — it tells uv which Python to actually use. Keep it tracked in git. It is not redundant withrequires-python.
If .python-version goes missing, uv picks the newest interpreter it can find (e.g. 3.14) and then fails the constraint:
error: The requested interpreter resolved to Python 3.14.2, which is
incompatible with the project's Python requirement: >=3.10, <3.14
Fix: don't widen the constraint — restore the selector.
1. Make sure .python-version exists at the repo root and contains 3.13.
2. Rebuild the environment on 3.13: uv venv --python 3.13 --allow-existing.
3. If your IDE still uses 3.14 (e.g. PyCharm running uv run), point its project interpreter at this repo's .venv/bin/python (which is 3.13), and clear any UV_PYTHON override in the run config.
Voting methods¶
Dispatched automatically from the file's voting_method (or from the ballot style — ranked A>B>C always routes to RCV-IRV):
- STAR — single-winner Score Then Automatic Runoff (the default). → learn
- Bloc STAR / proportional STAR (
bloc,sss,rrv,allocated) — multi-winner variants. → Bloc · proportional - Approval — score each candidate 0/1; most approvals wins. → learn
- Ranked Robin (
RankedRobin, aka RCV-RR / Copeland / Consensus) — every pair of candidates compared head-to-head; best win–loss record wins. Prints the full pairwise table and flags Condorcet cycles. → learn - RCV-IRV — ranked ballots, tabulated by the vendored RCV-IRV engine. → learn
Terminology: this repo says RCV-IRV (or IRV) for the instant-runoff count, reserving bare RCV for the ranked-ballot family. "RCV" loosely means IRV in US usage; we clarify once, then use the precise term. See Tips — Terminology: RCV vs IRV vs RCV-IRV (and friends).
Validation philosophy¶
There's no separate validation step — the same engine that tabulates is what catches bad files. Feed it a malformed election and, instead of a Python traceback, it prints a specific, plain-language error saying what's wrong (and usually how to fix it), then exits cleanly. For example: a missing ballots: block prints the key-components template; a ranked ballot under a score method explains the mismatch; a generated _tabulated.txt file is refused as input.
This error behavior is itself tested: the negative pytest cases feed in deliberately malformed files and assert each one produces the right message with no traceback — so the error experience can't silently regress.
The vendored engine¶
STARVote_LH_tabulation_engine/ is a fork of Larry Hastings' starvote, kept in-tree so the examples and tests pin a known-good engine. Local additions (extra reporting, the preference matrix, the [Divergence from STAR] block, the lot_numbers tie-break wiring, clearer errors) are documented in Fork Notes — starvote (vendored fork). Quick checks can use the system python3; the engines are vendored, not pip dependencies.