Document LoadData vote loading and migrate monitoring README to Markdown.

Record v3 vote-results.json as the tally source of truth, warn on
old-school txt/ini, and refuse v2 raw_board_votes.json. Point whatif
and the STV tests at the same loaders.

Co-authored-by: Grok <grok@x.ai>
diff --git a/README.md b/README.md
index d831a35..7acf14a 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,42 @@
 > this version. Read the [Getting Started Guide](v3/docs/quickstart.md)
 > to set up a development environment for v3.
 
+## STV tally and what-if
+
+Meek STV lives in [`monitoring/stv_tool.py`](monitoring/stv_tool.py).
+v3 tallies (`steve.vtypes.stv`) and the what-if CLI both call that module.
+
+**Load a vote file** with `LoadData.from_path` (a file, not a directory):
+
+| Input | Behavior |
+|---|---|
+| `vote-results.json` (v3 tally JSON) | Source of truth. No warning. |
+| `raw_board_votes.txt` (+ sibling `board_nominations.ini`) | Old-school. Warns on stderr. |
+| `raw_board_votes.json` (v2 export) | Not a tally format. Warns and exits. |
+
+```
+python3 monitoring/stv_tool.py path/to/vote-results.json
+python3 monitoring/stv_tool.py -s 9 -v path/to/raw_board_votes.txt
+```
+
+**What-if scenarios** (seats, drop a candidate, runoff-only set) are
+[`whatif.py`](whatif.py). The positional argv is fixed for `whatif.rb`:
+
+```
+python3 whatif.py [-v] VOTES_FILE [seats] [name ...]
+python3 whatif.py VOTES_FILE -Lastname
+```
+
+Names may be first, last, or compacted full name (case-insensitive).
+A leading `-` on a name removes that candidate; otherwise only the
+named candidates run. Warnings for legacy files go to stderr so they
+do not break `whatif.rb` grepping of `elected` lines.
+
+`whatif.rb` still consumes `raw_board_votes.txt`. Do not point it at
+v2 JSON. Prefer `vote-results.json` for new tallies.
+
+See also [`monitoring/README.md`](monitoring/README.md).
+
 ## Getting Started
 
 Getting Started documentation can be found at: http://steve.apache.org/demo.html
diff --git a/monitoring/README.md b/monitoring/README.md
new file mode 100644
index 0000000..2dd0497
--- /dev/null
+++ b/monitoring/README.md
@@ -0,0 +1,66 @@
+# Vote monitoring tools
+
+Helpers for processing closed-election tallies.
+
+When votes are closed, each monitor historically received an email: a
+final list of votes with hash ID and timestamp, time-ordered so later
+ballots sit at the bottom.
+
+## STV (`stv_tool.py`)
+
+Meek STV engine plus `LoadData` (load a **file**, not a directory).
+v3 (`steve.vtypes.stv`) and `whatif.py` (repo root) call this module.
+
+```
+./stv_tool.py vote-results.json
+./stv_tool.py -s 9 -v raw_board_votes.txt
+```
+
+| Input | Behavior |
+|---|---|
+| `vote-results.json` (v3 tally JSON) | Source of truth. `labelmap` + votestrings for the STV issue. |
+| `raw_board_votes.txt` (+ sibling `board_nominations.ini`) | Old-school emailed format. Non-vote lines ignored; later vote per 32-char hash wins. Warns on stderr. |
+| `raw_board_votes.json` (v2 export, labels like `AA`/`AB`) | Not a tally format. Warns and exits. Keep for posterity; pass txt or v3 JSON instead. |
+
+`whatif.py` runs seat / drop / runoff scenarios on the same loaders.
+`whatif.rb` still wants txt+ini.
+
+## Other STV tools
+
+OpenSTV or the deprecated VoteMain systems. Most modern tools use BLT.
+
+Feed the STV-tally email and write an output file:
+
+```
+./nstv-rank.py raw_votes.txt > outputFile
+```
+
+After installing Voting Systems Toolbox:
+
+```
+java -cp Vote-0-4.jar VoteMain -system stv-meek -seats 9 outputFile
+java -cp Quick_STV_1_2.jar VoteMain -system stv-meek -seats 9 outputFile
+```
+
+BLT-oriented tools such as OpenSTV:
+
+```
+./nstv-rank.py -b raw_votes.txt > outputFile.blt
+```
+
+ASF Meek STV settings: precision 6; threshold Droop | Dynamic | Fractional.
+
+## YNA
+
+`yna-summary.pl` tallies Yes / No / Abstain. You can concatenate all
+final YNA tallies into one file; it pulls out each issue name and
+result, honoring only the most recent vote per voter.
+
+```
+./yna-summary.pl all30tally.txt
+```
+
+## Voter checks
+
+`monitoring-check.pl` checks that incoming votes are from legitimate
+voters.
diff --git a/monitoring/README.txt b/monitoring/README.txt
deleted file mode 100644
index 74c79e9..0000000
--- a/monitoring/README.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-Included are some helpful tools/scripts to make the
-vote monitor's job easier.
-
-When votes are closed, each monitor will get, via EMail,
-a final tally (list) of all votes cast, with hash ID and
-timestamp. The list is time-ordered as well, so the later-
-cast ballots are closer to the bottom of the tally.
-
-For STV, we use voter/stv_tool.py to process the vote results:
-
-    ./stv_tool.py raw_votes.txt
-
-where `raw_votes.txt` is the emailed set of votes. For example,
-see Meetings/.../raw_board_votes.txt. Lines other than votes
-are ignored, and the votes are assumed to be time-ordered to
-ensure that only the latest vote by each voter is considered.
-
-We also support OpenSTV (www.openstv.org) or the deprecated VoteMain
-(http://sourceforge.net/projects/votesystem) systems. Most modern
-tools use the blt format.
-
-Simply feed as input the STV-tally email (typically used for the board
-elections) and direct the output to 'outputFile' (or whatever
-you'd like):
-
-   ./nstv-rank.py raw_votes.txt > outputFile
-
-After installing Voting Systems Toolbox, you can execute the 'VoteMain'
-program as
-
-    java -cp Vote-0-4.jar VoteMain -system stv-meek -seats 9 outputFile
-        or
-    java -cp Quick_STV_1_2.jar VoteMain -system stv-meek -seats 9 outputFile
-
-where outputFile is the output of nstv-rank.py above.
-
-Using blt-oriented STV tools, such as OpenSTV:
-
-   ./nstv-rank.py -b raw_votes.txt > outputFile.blt
-
-and load in the blt file to OpenSTV. Please note the ASF uses Meek STV with:
-
-    Precision: 6
-    Threshold:  Droop | Dynamic | Fractional
-
-----
-
-For simple YNA votes (Yes / No / Abstain), we have
-
-    yna-summary.pl
-
-which does the checks for you.
-
-This script is smart enough that you can actually concat
-*all* the final vote tallies for yna elections into one big
-file, and it will pull out the issue name and the results
-for each issue.
-
-   ./yna-summary.pl all30tally.txt
-
-yna-summary.pl will only honor the most recent vote cast by each voter.
-
-
-----
-
-Also see monitoring-check.pl to ensure the incoming votes are from
-legitimate voters.
diff --git a/v3/tests/README.md b/v3/tests/README.md
index 059af96..51cb6e5 100644
--- a/v3/tests/README.md
+++ b/v3/tests/README.md
@@ -27,8 +27,8 @@
 
 ### Prerequisites
 
-- Ensure the `stv_tool` module is available at `../../../monitoring/stv_tool.py` (relative to `v3/steve/vtypes/stv.py`).
-- Provide a `Meetings` directory containing subdirectories (e.g., `yyyymmdd`) with the required input files: `raw_board_votes.txt` (raw vote data) and `board_nominations.ini` (label mappings for candidates).
+- Ensure the `stv_tool` module is available at `../../../monitoring/stv_tool.py` (relative to `v3/steve/vtypes/stv.py`). Live tallies prefer v3 `vote-results.json` via `LoadData.from_path`. These regression tests still use historical `raw_board_votes.txt` so v3 continues to match pre-v3 Meek results (the loader will warn that txt is old-school).
+- Provide a `Meetings` directory containing subdirectories (e.g., `yyyymmdd`) with `raw_board_votes.txt` and `board_nominations.ini`.
 
 ### Scripts
 
@@ -43,9 +43,10 @@
 
 ### Dependencies
 
-- `raw_board_votes.txt`: Contains the raw vote data in each meeting subdirectory.
-- `board_nominations.ini`: Contains the label mappings for candidates in each meeting subdirectory.
+- `raw_board_votes.txt`: Historical raw vote data in each meeting subdirectory (legacy format; `read_votefile` takes only the filename).
+- `board_nominations.ini`: Letter-to-name map for those txt files.
+- Do not pass v2 `raw_board_votes.json` into `stv_tool`; it is not a tally format.
 
 ### Importing stv_tool
 
-The `stv.py` module imports `stv_tool` from `../../../monitoring/stv_tool.py` using dynamic loading to ensure compatibility.
+The `stv.py` module imports `stv_tool` from `../../../monitoring/stv_tool.py` using dynamic loading. Tally math is `run_stv`; file loading for new code is `LoadData.from_path`. See also [`monitoring/README.md`](../../monitoring/README.md).