Everything needed to work on Social Simulation Arena: architecture, data sources with APIs, submission format, scoring spec, roadmap, and guardrails. Plain language on purpose.
The MVP (minimum viable product, the smallest version that works) is deliberately boring: one GitHub repo, scheduled GitHub Actions, flat JSON files, and a static site. No servers, no database. TS-Arena and ForecastBench both run on pipelines this simple, and it keeps every artifact public and auditable by construction.
The repo is live: github.com/jajamoa/social-sim-arena. This is the actual layout:
social-sim-arena/
├── questions/season0.json frozen round definitions (id, lock_at, release_at, resolve rule)
├── ssa/
│ ├── adapters/votehub.py poll-level JSON, no key (approval + generic ballot)
│ ├── adapters/fredcsv.py Michigan sentiment via keyless FRED CSV
│ ├── average.py house-effect-adjusted, recency-weighted average
│ ├── baselines.py persistence + clamped straight-line trend (OLS)
│ ├── scoring.py CRPS / RPS / skill (unit-tested)
│ └── refresh.py orchestrator: fetch, compute, write site/data.json
├── forecasts/<round>/<entrant>.json submissions, via pull request
├── entrants/<entrant_id>.json agent registry (register by PR, schema-checked)
├── resolutions/resolved.json ground truth as rounds resolve
├── schema/forecast.schema.json submission contract
├── tools/validate_submission.py run locally and by CI
├── tests/test_scoring.py hand-checked scoring values
├── site/ entry page + data.json (generated)
└── .github/workflows/ refresh.yml (daily cron), validate.yml (PR check)
refresh.yml runs every 6 hours: pulls VoteHub and FRED, recomputes averages and live baselines, commits site/data.json. The entry page reads that file, so the site is always as fresh as the last cron run.validate.yml runs on every pull request touching forecasts/: schema check, round exists, and now < lock_at. A submission after the lock fails CI. Commit time is the timestamp; the sha256 of the canonical JSON is the fingerprint.resolutions/resolved.json (manual for now, cron later); the next refresh scores every entrant with CRPS and skill and publishes the leaderboard in data.json.Everything is a commit. The audit trail is the git history itself, which is the cheapest possible pre-registration.
Ranked by how well each source fits a lock-before-release protocol. Gallup is gone: it ended its 88-year presidential approval series in February 2026, so nothing here depends on it.
| Source | Cadence | Access | Crosstabs | Role |
|---|---|---|---|---|
| Michigan Surveys of Consumers | 2x monthly, preannounced 10:00 ET | site tables + FRED API | partial | primary (best calendar) |
| Economist / YouGov | weekly, publishes ~Tue | topline + crosstab PDFs | yes, rich | primary (best crosstabs) |
| Morning Consult | weekly, ~Mon | public HTML report | partisan cuts | primary |
| Civiqs | daily | dashboard only | deep, interactive | secondary |
| VoteHub | continuous averages | free JSON API | no | resolution input |
| Silver Bulletin | continuous averages | page + CSV download | no | resolution input |
| RealClearPolling | continuous averages | web tables, blocks bots | no | resolution input (manual) |
| Pew Research | irregular | reports + toplines | yes | cross-checks only (no fixed calendar) |
| Certified election results | Nov 3+ | AP / state officials | n/a | midterm specials |
| Commissioned fresh questions | quarterly | YouGov omnibus / Prolific | on request | gold standard |
Poll-level data with pollster, field dates, sample size, population, and answers. No key needed.
GET https://api.votehub.com/polls?poll_type=approval
GET https://api.votehub.com/polls?poll_type=generic-ballot
Averages at votehub.com/polls, methodology page describes recency and pollster-quality weighting. This is our machine-readable resolution input. One caveat found in testing: the API snapshot lags the live site by a few weeks, so the pipeline stamps every derived number with its true as-of date.
GET https://fred.stlouisfed.org/graph/fredgraph.csv?id=UMCSENT
Series UMCSENT (index) and MICH (inflation expectations). Caveat: FRED lags one month at the source's request. For release-day values scrape the official tables at data.sca.isr.umich.edu; the release calendar with exact dates is on sca.isr.umich.edu (next: preliminary Aug 14, final Aug 28, both 10:00 ET).
Archive hub: The Economist/YouGov polls topic page. Each wave links two PDFs (toplines, crosstabs) hosted on cloudfront, names like econTabReport*.pdf. Collects responses Friday to Monday, publishes Tuesday or Wednesday, about 1,600 US adult citizens per wave. Parse with pdfplumber; the table layout is stable week to week. This is the main crosstab target: party, age, gender, race cuts.
Tracker hub: Tracking Public Opinion of Trump's Washington. Weekly report is a public HTML file (pattern MCPI-PI-Weekly_YYMMDD.html on pro-assets.morningconsult.com), roughly 2,200 registered voters, with partisan crosstabs and a generic ballot number. Straightforward HTML parse.
Daily tracker with deep subgroup filters at civiqs.com/results/approve_president_trump_2025. No CSV or API, so we treat it as a secondary target: one weekly snapshot round resolved on the Friday dashboard value, recorded manually or with a light scraper that respects their terms.
Silver Bulletin generic ballot page offers a CSV of every poll in its database; it also runs a Trump approval average. RealClearPolling publishes averages as web tables but returns 403 to scripts, so values are entered manually if used. Resolution for average-based questions uses at least two independent averages.
Quarterly rounds where the question has never been asked anywhere, so it cannot be in any training set. Costs are small: YouGov omnibus runs about $300 per closed question on a representative sample (about 200 entry fee plus per-question fees, n = 1,000 to 2,000, next-day results). A Prolific census-matched sample costs roughly $1,300 to $1,500 for a five-minute battery at n = 1,000 including the 42.8% platform fee. A full probability-sample poll would run $30k+ and is not needed for the MVP.
Register first: one PR adding entrants/<entrant_id>.json (id, name, type, method; see schema/entrant.schema.json). The arena site has one-click prefilled GitHub links for both registration and forecasts.
A round is one question about one scheduled release. A submission is a distribution over the answer, filed before the lock. Submitting = opening a pull request that adds one JSON file; validate.yml enforces the deadline and the schema (schema/forecast.schema.json), and prints the canonical sha256. Validate locally first:
python tools/validate_submission.py forecasts/<round_id>/<entrant>.json
// questions/yougov-2026-w34-approval.json
{
"round_id": "yougov-2026-w34-approval",
"tracker": "economist_yougov",
"question": "Trump job approval, % approve among US adults",
"release_at":"2026-08-18T14:00:00Z (est.)",
"lock_at": "2026-08-16T14:00:00Z",
"resolve": "topline PDF; crosstabs scored separately",
"crosstabs": ["party_id", "age_4way", "race_4way"]
}
// forecasts/yougov-2026-w34-approval/gpt-5.5_web.json
{
"entrant": "gpt-5.5_web",
"topline": { "mean": 41.2, "sd": 1.4 },
"crosstabs": {
"party_id": { "dem": {"mean": 8.5, "sd": 1.5},
"ind": {"mean": 35.0, "sd": 2.0},
"rep": {"mean": 84.0, "sd": 2.0} }
},
"notes": "harness v0.3, temperature 0, 3 samples, median"
}
Point guesses are not accepted. Two formats, both scored with CRPS so they compete fairly: a normal (mean + sd), or a quantile set for skew and fat tails, e.g. "quantiles": {"0.05": 37.0, "0.25": 39.2, "0.5": 40.1, "0.75": 41.0, "0.95": 42.8} (must include the median). The canonical JSON is hashed (sha256) and the hash plus commit timestamp is the citation.
1 - CRPS(entrant) / CRPS(persistence), averaged over resolved rounds. Positive = better than copying the last release. Weather forecasters make the same move, scoring against 'same as yesterday' (persistence) and the seasonal average (climatology).Every LLM runs in the same harness: same prompt template, temperature 0, three samples, median taken, full logs committed. Two arms (versions) per model, the same setup LLM-SoccerArena used: one closed book, one with web access. SoccerArena found web access improved the Brier score (an accuracy score for probability forecasts; lower is better) by only 0.023, and whether that holds for opinion is itself a result.
An agent can live entirely on its own schedule: read the open rounds and live data from two public JSON files, produce forecasts, open a pull request. Copy templates/agent-cron.yml into your own repo and replace one step with your model; it then submits automatically every day until season end. The arena's own baselines run exactly this way: the refresh cron re-files their forecasts every 6 hours while a round is open, and the last commit before lock counts.
The human baseline is a paid panel of 5 to 10 forecasters on a question subset. This is not optional decoration: at Metaculus, pro forecasters beat the bot teams every quarter through Q2 2025, and ForecastBench's published leaderboards keep superforecasters ahead of the best LLM. A leaderboard without humans has no credibility anchor.
resolutions/resolved.json instead of manual entry.Ping Chance. Code and issues: github.com/jajamoa/social-sim-arena.