98 lines
7.1 KiB
Markdown
98 lines
7.1 KiB
Markdown
# Fantasy Politics
|
||
|
||
A private fantasy election game for friends, built in C++23 with the shared **Modulus**, **Ligo**, and **Noto/SQLite** libraries. The frontend uses TypeScript and Vite.
|
||
|
||
The app now has username/password accounts, invitation-based registration, private leagues, timed snake drafts, private queues, rosters, and provisional standings. The main navigation is **Leagues / Candidates / Results**. The earlier scoring simulator lives separately at `/lab.html`, with its APIs enabled only by `--allow-demo`.
|
||
|
||
**Real drafts remain locked.** The included official catalog has 115 candidates in 30 Michigan/North Carolina races. National candidate coverage, sourced ratings, and live count/outcome verification are still incomplete. League creation and invitations work before those data gates clear. There is no deployed site yet.
|
||
|
||
## Start locally
|
||
|
||
Use the sibling `modulus`, `noto`, and **updated `ligo`** checkouts in this workspace. This change extends Ligo's HTTP API; an older published `ligo/latest` package cannot build this app. The workspace helper exports the local recipes into your Conan cache and builds against those revisions, without publishing anything.
|
||
|
||
Prerequisites: a C++23 compiler (the configured target is GCC 12), CMake 3.25+, Ninja, Python 3, Conan 2.26.2, Node 22.12+, and network access for initial dependencies. CI uses Node 24.
|
||
|
||
```sh
|
||
conan profile detect # once, if you do not already have a profile
|
||
python3 tools/build_workspace.py
|
||
npm --prefix frontend ci
|
||
npm --prefix frontend run build
|
||
|
||
build/workspace_debug/fpol_admin import data/real/us-2026-10-04-partial.json
|
||
build/workspace_debug/fpol_admin create-user YOUR_USERNAME
|
||
# Password is prompted privately; use at least 12 characters.
|
||
|
||
build/workspace_debug/fpol_server --web-root frontend/dist
|
||
```
|
||
|
||
Open **http://127.0.0.1:8080**. The first account created through the local CLI is an administrator. Friends create accounts through private league invitations. All commands default to `var/fpol.sqlite`; use `--database PATH` consistently to select another database.
|
||
|
||
For Vite development, start the backend with `--origin http://127.0.0.1:5173`, then run `npm --prefix frontend run dev`. Use the exact configured origin, including hostname and port. For Vite preview use origin `http://127.0.0.1:4173`. Cookie security defaults suit loopback development; production uses HTTPS and `--secure-cookies`.
|
||
|
||
The existing Modulus workflow remains available once the matching Ligo package is in its Conan cache/remote:
|
||
|
||
```sh
|
||
python3 modulus.py configure --all
|
||
python3 modulus.py build --all
|
||
python3 modulus.py test --all
|
||
```
|
||
|
||
`modulus.py` remains byte-identical to the canonical bootstrap. Do not use `configure --update` to replace the local Ligo revision with an older published API. The [deployment guide](docs/deployment.md) describes a source-based Compose build that includes all four checkouts.
|
||
|
||
## Try a complete practice draft
|
||
|
||
```sh
|
||
build/workspace_debug/fpol_admin import data/demo/catalog.json data/demo/ratings.json \
|
||
data/demo/results-0.json data/demo/results-1.json data/demo/results-2.json \
|
||
data/demo/results-3.json data/demo/results-4.json
|
||
build/workspace_debug/fpol_server --web-root frontend/dist --allow-demo
|
||
```
|
||
|
||
Create a league and choose the **Practice** candidate pool. Invite a second player, then start from the draft room. Demo data stays explicitly separate from real data. The legacy simulator replays its five synthetic snapshots; those old snapshots are not automatically published as live league results.
|
||
|
||
## Rules
|
||
|
||
Leagues have 2–12 teams and 12 snake-draft rounds. Every team has Democrat, Republican, Other, and Flex slots for each of House, Senate, and Governor. A pick fills its party slot before Flex. Candidates are exclusive within a league; legal-pick checks preserve enough candidates for every remaining roster slot.
|
||
|
||
The server owns the 120-second clock. A timeout takes the first legal private-queue candidate, otherwise a uniformly random legal candidate. Commissioners can pause/resume. A process restart pauses active drafts; resuming gives the current picker a fresh 120 seconds. Browser draft views poll every 2 seconds; results and standings poll every 15 seconds.
|
||
|
||
`(November first-round / first-choice vote share percentage + 50 for the eventual called/certified winner) × frozen race rating`
|
||
|
||
Safe ×1, Likely ×1.1, Lean ×1.25, Toss-up ×1.5. Each candidate is rounded half up to two decimals before summing. Counts and outcomes are independent: a runoff changes the winner bonus while November share stays intact. Call retractions remove the bonus; corrections can lower points. Missing counts/ratings are unknown, not zero. Provisional rankings show reporting coverage beside known subtotals. Catalog, ratings, order, and rules freeze at draft start. Trades and waivers remain a later milestone.
|
||
|
||
## Election data and operations
|
||
|
||
The C++ `fpol_ingest` worker supports reviewed Open America count mappings, a normalized official-source JSON contract, candidate-source change capture, and a civicAPI capture/probe path. **civicAPI normalization is not implemented until a real response/schema has been validated.** No feed is configured or enabled automatically.
|
||
|
||
Changed raw payloads and normalized observations are immutable. Failed/ambiguous responses are quarantined; the last good counts remain visible with stale provenance. Replaying a file cannot verify a source or publish to a real catalog. Operators must obtain a successful live capture, review mappings, and explicitly activate coverage.
|
||
|
||
See [ingestion and source contracts](docs/ingestion.md), [source coverage](docs/data-coverage.md), the [506-contest inventory](data/contests_2026.json), and the [state/office coverage matrix](data/coverage_2026.csv). Official lists can change; review again before freezing. `tools/generate_real_catalog.py` reproduces the reviewed NC import and combined catalog; it does not fetch fresh evidence or claim a new review date.
|
||
|
||
Password recovery is operator assisted:
|
||
|
||
```sh
|
||
build/workspace_debug/fpol_admin reset-link USERNAME
|
||
```
|
||
|
||
Give that user `https://YOUR_HOST/#reset/TOKEN` using the returned token. It expires in 30 minutes, works once, and revokes all existing sessions on use. There is no email service. See [the API contract](docs/application-api.md) for session, CSRF, and request details.
|
||
|
||
## Verify
|
||
|
||
```sh
|
||
python3 tools/build_workspace.py --test
|
||
npm --prefix frontend run build
|
||
node tests/browser.mjs build/workspace_debug/fpol_server
|
||
python3 modulus.py format --check
|
||
```
|
||
|
||
The browser smoke test uses installed Chromium (`FPOL_CHROMIUM` can select a binary), Node's standard library, two isolated browser sessions, and the real C++ server. Screenshots go to ignored `build/browser-artifacts/`.
|
||
|
||
Socket-independent checks can be run separately:
|
||
|
||
```sh
|
||
python3 tests/test_core.py build/workspace_debug/fpol_admin
|
||
python3 tests/test_application.py build/workspace_debug/fpol_admin build/workspace_debug/fpol_ingest
|
||
```
|
||
|
||
They do not replace HTTP/browser checks. [The verification record](docs/verification.md) states exactly what ran here and what remains blocked. [Deployment and backup instructions](docs/deployment.md) cover persistent storage, Caddy HTTPS, bootstrap accounts, online backups, and restore.
|