Files
product-fpol/README.md
T
khodowany 78b2c383f1
ci / frontend (push) Successful in 9m13s
ci / backend (push) Failing after 6m40s
Game Features
2026-10-04 23:20:06 -05:00

7.1 KiB
Raw Blame History

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.

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:

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 describes a source-based Compose build that includes all four checkouts.

Try a complete practice draft

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, source coverage, the 506-contest inventory, and the state/office coverage matrix. 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:

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 for session, CSRF, and request details.

Verify

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:

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 states exactly what ran here and what remains blocked. Deployment and backup instructions cover persistent storage, Caddy HTTPS, bootstrap accounts, online backups, and restore.