BPML ↔ library reconciliation¶
Level: reference · deep dive
Generated by reconcile_bpml.py from committed snapshots of the two Drive sheets taken 2026-08-16, joined against the live registry. The snapshots are point-in-time copies, not a live feed — re-snapshot before trusting a number that matters.
The headline¶
| BPML process rows | 111 |
| …citing a test id | 11 |
…recording a gap (missing…) |
6 |
| …with no test reference at all | 93 (84%) |
| Distinct test ids cited by BPML | 11 |
| …that exist as a runnable case in this repo | 1 |
| …that exist only in the Drive test sheet | 1 |
| …that exist in neither | 9 |
| Case rows in the library registry | 276 |
| …distinct BV elections behind them | 163 |
| …referenced by any BPML row | 1 |
| …referenced by nothing in the sheet | 162 (99%) |
(The registry holds 276 rows but 163 distinct ids: a multirace election backs several cases that all carry its one test id.)
What the numbers mean¶
The sheet's coverage column is 84% empty — which is honest, not a failure: it was built as a backlog. The problem is the other direction. The library holds 276 runnable BV-backed case rows (163 distinct elections) and the sheet references 1 of them, so 99% of the testing work this project has already done is invisible to the document that is supposed to track testing coverage.
The two inventories also barely overlap by construction:
- Drive test-sheet ids not in the library: 68
- Library cases not in the Drive test sheet: 152
The Drive sheet is the older manual-QA range and stops in the low hundreds; the library's ids run to BV2284. They are not two views of one inventory — they are two inventories.
Why the join returns almost nothing¶
Not neglect — disjoint subject matter. Split the sheet's rows by what they are actually about:
| rows | ||
|---|---|---|
| BPML rows about the application | 102 | 92% |
| BPML rows about counting votes | 9 | 8% |
| Library elections, every one about counting votes | 163 | 100% |
The sheet is overwhelmingly an application inventory — create an election, change a race, upload voters, download a CSV, log in, archive. The library is entirely a tabulation inventory — given these ballots, is this the right winner. Those are two different testing activities, and no single test-id column can be the coverage map for both.
The nine rows where they genuinely meet:
| L1 | Process | Currently cites |
|---|---|---|
| Election | Verify Preference Matrix | BV705 |
| Voting Methods | Ranked Robin - Single Winner | BV1550 |
| Voting Methods | Single-Winner - Approval | — |
| Voting Methods | Single-winner STAR - scoring/runoff divergence | BV90 |
| Voting Methods | Plurality (chose one) - Single-winner | — |
| Voting Methods | Multi-winner - Bloc STAR | — |
| Voting Methods | Multi-winner Plurality | Multi-winner Plurality |
| Establish Election Procedures | Handling ties | — |
| Tabulation | Distribution of Equal Support report | — |
The three cross-cutting rows, curated¶
Six of the nine seam rows map onto a single method family. These three do not — their evidence is scattered across methods, so the list is curated by hand and every entry is re-validated against the registry on each run.
Handling ties — 10 cases
| Test id | Case | Method | Why it's on the list |
|---|---|---|---|
BV126 |
bv126_ties_every_step_8fvd2x | STAR | every tiebreak rung fires in one election |
BV2276 |
bv2276_qhjyr2_second_finalist_tie | STAR | tie for the second runoff slot |
BV830 |
bv830_vb3xv2_no_condorcet_tie_score | STAR | tied with no Condorcet winner |
BV129 |
bv129_score_tiebreak_bloc | Bloc STAR | score tiebreak, multi-winner |
BV130-r2 |
bv130r2_dead_rung_bloc | Bloc STAR | a rung that cannot decide |
BV750 |
bv750_tie_breaking_bloc | Bloc STAR | bloc tie-breaking end to end |
BV2141 |
bv2141_3r3yf7_four_degree_tie | RankedRobin | four-way tie on the Copeland record |
BV2261 |
bv2261_y2fbpc_tiebreak_recorded_draws | RankedRobin | BV's seeded shuffle is recorded and replayable |
BV2262 |
bv2262_2gvwr9_nine_way_dead_heat | RankedRobin | nine-way dead heat, all positions recorded |
BV2257 |
lunch_choose_one_dead_tie | Plurality | Choose-One dead tie |
Distribution of Equal Support — 5 cases
| Test id | Case | Method | Why it's on the list |
|---|---|---|---|
BV11 |
bv11_6xhfp8_full_equal_support | STAR | every ballot scores both candidates equally |
BV2283 |
bv2283_hb4qvv_all_equal_recheck | STAR | all-equal ballots, re-checked against BV |
BV655 |
bv655_jfrk9t_equal_opposition | STAR | equal support on opposed blocs |
BV2219 |
bv2219_36f4v2_equal_opposite_base | STAR | the base of the equal/opposite pair |
BV2220 |
bv2220_q8q9m7_equal_opposite_plus_cancel | STAR | same election plus cancelling ballots |
Preference Matrix — 8 cases
| Test id | Case | Method | Why it's on the list |
|---|---|---|---|
BV2270 |
bv2270_8h4bvh_head_to_head_vs_margin | RankedRobin | head-to-head result vs margin -- the matrix's two readings |
BV2140 |
bv2140_48hjkv_most_pairwise_wins | RankedRobin | most pairwise wins across the grid |
BV2260 |
bgg9qh9_most_wins_is_not_condorcet | RankedRobin | most wins is NOT the Condorcet winner |
BV2157 |
bv2157_mmcmpy_condorcet_cycle_rps | STAR | a cycle -- the hardest thing the grid has to render |
BV2156 |
bv2156_3grpbb_star_misses_condorcet | STAR | STAR's winner differs from the grid's |
BV2131 |
bv2131_tennessee_condorcet_center_vqyqkr | RankedRobin | the Tennessee grid, centre candidate |
BV2250 |
condorcet_1788_star | STAR | one historical election, three methods on one grid |
BV1525 |
bv1525_condorcet_loser_bloc | Bloc STAR | Condorcet loser seated under bloc |
The consequence for the sheet: it should not gain 162 rows. It should gain nine links. A counting row's verification is a whole family of elections in the library, not one id — so it points at the by-method index, and the library's own count is the coverage number. See SCOPE.md for the re-scope and the resulting column spec.
BPML rows that DO have a runnable case¶
| Test id | BPML process | Library case |
|---|---|---|
BV15 |
Create - Single-Winner - Plurality | bv15_4h89vj_plurality_abstain |
BPML rows citing a test id with no runnable case¶
These are the sheet's real coverage gaps — a process it believes is tested, where nothing in the library reproduces it.
| Test id | BPML process | Where it exists |
|---|---|---|
BV90 |
Single-winner STAR - scoring/runoff divergence | Drive test sheet only |
BV485 |
Election Admin - Ballot View | nowhere |
BV486 |
Preserve Ballot Secrecy | nowhere |
BV600 |
Download CVR | nowhere |
BV610 |
CSV Download - min 2 races | nowhere |
BV615 |
CSV Download - 1 race | nowhere |
BV630 |
(unnamed - placeholder) | nowhere |
BV705 |
Verify Preference Matrix | nowhere |
BV1380 |
Confirmation code - Regular Ballot | nowhere |
BV1550 |
Ranked Robin - Single Winner | nowhere |
Rows the sheet already flags as gaps¶
The sheet doing its job — these cells are the most useful thing in it.
| BPML process | Marker |
|---|---|
| Delete - entire election | missing |
| Mark ballot Abstain - Election Level | missing functionality |
| Mark ballot Abstain - Race Level | missing functionality |
| Confirmation code - Editable Ballot | missing document |
| Void Ballot - Regular | missing document |
| Void Ballot - Editable Ballot | missing document |
Process rows by area¶
| L1 area | rows |
|---|---|
| Election | 34 |
| Ballot | 11 |
| Reports | 10 |
| Voter | 6 |
| Voting Methods | 6 |
| Candidates | 5 |
| Browse | 4 |
| Electors (Voters) | 4 |
| Authentication System | 3 |
| Poll | 3 |
| Security | 3 |
| Upload Election | 3 |
| Abstain | 2 |
| Graphical User Interface | 2 |
| Login | 2 |
| Registration | 2 |
| Audit | 1 |
| Certification | 1 |
| Communications | 1 |
| Establish Election Procedures | 1 |
| Precincts | 1 |
| Proxy Vote | 1 |
| Support Voter | 1 |
| Tabulation | 1 |
| Training | 1 |
| Translation | 1 |
| Vote Type | 1 |
Related: the docs information architecture · BV registry · all cases by method