# Fantasy Politics `product-fpol` is a private, noncommercial fantasy politics game for friends around the 2026 US midterms. This first playable development slice lets you browse candidates, build a 12-slot party, check league capacity and replay fictional election-night scoring. The backend is **C++23**, with **Modulus** for builds, **Ligo** for HTTP, and **Noto/SQLite** for immutable catalog, rating and results snapshots. The frontend is TypeScript/Vite. Accounts, live drafts, cross-team ownership, trades, automatic data ingestion and deployment are later milestones. ## Start developing Prerequisites: Linux, Docker available to your user, Python 3, and Node.js 22.12+ with npm (CI uses Node 24). Initial setup requires network access for public Conan packages, the build image and npm dependencies. Sibling library checkouts are not required. ```sh python3 modulus.py configure python3 modulus.py build ``` Seed the development database with explicitly fictional candidates, ratings and results. Run the built admin executable from the repo root in the build environment, or directly on a compatible Linux host: ```sh build/build_x86_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 # Optional: a separate, partial real catalog. It has no real ratings/results. build/build_x86_debug/fpol_admin import data/real/mi-2026-10-04.json python3 modulus.py run ``` In another terminal: ```sh cd frontend npm ci npm run dev ``` Open **http://127.0.0.1:5173**. Select the fictional catalog, click **Try an example roster**, then **Play replay**. Search/filter the field, change picks and compare points with/without race multipliers. Vite proxies `/api` to port 8080. Restart the backend after C++ changes; refresh the browser after imports. The roster is an in-page sandbox and is cleared on refresh or catalog change. Data is stored at `var/fpol.sqlite` (ignored by Git), relative to the working directory. Both executables accept `--database PATH`; use the same path when importing and serving. Importing the same document again is harmless. Corrections require new snapshot IDs, preserving the earlier result for replay. `python3 tools/generate_demo.py` deterministically regenerates only fictional fixtures. The standard Modulus runner launches `fpol_server` inside its container, publishes port 8080 only on host loopback, and mounts the repository so the development database persists. For local direct builds, launch the built server executable instead. There is no hosted environment or authentication yet. ## Scoring to try Every office — House, Senate, Governor — has a Democrat, Republican, Other and Flex slot. Other includes independent and third-party candidates; Flex takes any affiliation in that office. Each candidate can appear only once in a roster, and opposing candidates may both be picked. `(vote share percentage + 50 if called/certified winner) × frozen race multiplier` | Rating | Multiplier | Example | | --- | --- | --- | | Safe | 1.00 | 80% winner → 130 points | | Likely | 1.10 | 60% winner → 121 points | | Lean | 1.25 | 54% winner → 130 points | | Toss-up | 1.50 | 51% winner → 151.5; 49% loser → 73.5 | Candidates are rounded half up to two decimals before summing. Reporting shares score without a win bonus; the current-leader preview is hypothetical and displayed separately. A missing count/rating is unknown, never an invented zero. Only a filled, fully scored roster gets a complete total. Corrections can retract a call. The replay demonstrates unreported votes, early totals, changing leads, calls and corrections. All five frames are synthetic, not a historical backtest. ## Admin and API ```sh build/build_x86_debug/fpol_admin --help build/build_x86_debug/fpol_admin capacity data/demo/capacity.json build/build_x86_debug/fpol_admin score-preview data/demo/preview.json build/build_x86_debug/fpol_admin catalog ``` The last three commands use the same C++ handlers as `POST /api/capacity`, `POST /api/score-preview` and `GET /api/catalog`. `GET /api/rules` describes the slots and scoring. `GET /api/health` is process liveness. Ligo returns `{ "ok": true, "payload": ... }`, or an error envelope. No admin write endpoint is exposed. See [the data/API contract](docs/data-contract.md) for schemas, immutable import behavior, input bounds and round handling. The capacity report checks the per-party and total candidate supply for 2–12 players; it does not certify ratings or results coverage. ## Data readiness **The real league is not ready to draft.** The included Michigan snapshot contains 73 candidates in 15 races; the remaining states, real ratings and live result/call coverage are unresolved. Switching to that catalog shows those gaps rather than fictional replacements. An official candidate list may change and must be reconciled before use. Read the dated [source feasibility report](docs/data-coverage.md) and [office/state coverage matrix](data/coverage_2026.csv). Free automatic updates remain a launch requirement. No provider transport or national coverage is claimed verified, and no background scraper/poller has been enabled. Runoff/ranked-choice resolution also requires a settled policy before affected races can launch. ## Serve the built frontend ```sh npm --prefix frontend run build python3 modulus.py run -- --web-root frontend/dist ``` Open **http://127.0.0.1:8080**. Assets load at startup, so restart after rebuilding. Node is not needed to serve built assets. The server accepts `--host`, `--port`, `--web-root`, `--database` and `--help`; defaults are `127.0.0.1:8080`, API-only, and `var/fpol.sqlite`. Port 0 selects an available port. SIGINT/SIGTERM stop it cleanly. Exact registered static paths are served; hidden files, symlinks and `/api` asset collisions are excluded. Use only Vite output as the web root. `npm --prefix frontend run preview` is available on port 4173 with the same API proxy. ## Verify changes ```sh python3 modulus.py configure --all python3 modulus.py build --all python3 modulus.py test --all python3 modulus.py format --check npm --prefix frontend run build ``` CTest's `product_fpol` suite runs the domain/persistence tests through the production admin executable, then HTTP process tests for API envelopes, validation, static assets, startup errors, occupied ports and shutdown. The frontend build includes strict TypeScript checking. For socket-restricted environments, core checks can also run independently: ```sh python3 tests/test_core.py build/build_x86_debug/fpol_admin ``` Passing core checks does not replace the HTTP suite. See [the local verification record](docs/verification.md) for the compiler and environmental limitations of this implementation session. CI retains the shared GCC 12 debug/release builds and full test suite. Configure generates ignored editor support; customize `modulus.json` and refresh with `python3 modulus.py lsp`. C++ and Python follow shared Modulus formatting; run `python3 modulus.py format` before committing. The checked-in bootstrap remains byte-identical to Modulus's canonical bootstrap. `configure --update` refreshes tooling/packages; `--no-remote` needs an already-populated project cache. CMake installs both executables; distribute frontend assets separately. ## Next milestone Clear the data gates first. Then add the private league and invitations, persist team ownership, freeze the chosen catalog/ratings before a live snake draft, add trades/locks, and connect verified polling to the existing snapshot/import layer. Election-night standings can reuse the deterministic scorer and correction history. Keep product rules here and reusable infrastructure in the shared library repos.