Design concept and design system in docs/design/: principles (evidence
first, neutral, fair to the people shown, plain language, time as a
dimension, accessible and private), information architecture, core
patterns and a concept for every page; tokens with checked contrast,
component catalogue, content and accessibility rules.
web:
- neutral ink-indigo accent instead of flag red; party colours only as
small markers next to the name; light/dark theme with a switcher
- stylesheets split into tokens, base, layout, components, print
- new header (search, filter count, language, theme, mobile menu) and
footer; breadcrumbs, page headers, tabs with counts, filter bars that
collapse on phones, pagination, notices and empty states
- claim cards with source meter, legal stage, innocence notice inside
the card and a permalink; claim pages with citation (/angaben/{id})
- profiles with role timeline and key figures; party and organisation
dashboards; vote list with position chips; calendar agenda on phones;
tables that stack into cards; loading and error pages; favicon
backend:
- GET /api/v1/claims/{id} and MCP tool get_claim for permalinks
- parties list: evaluate person_status once instead of per party
(30 s -> 1 s on the sample database, identical result)
- parliament calendar: HTML-decode titles ("84. Sitzung")
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.8 KiB
5.8 KiB
AGENTS.md
Guidance for AI coding agents working on this repository. Humans: start with README.md and CONTRIBUTING.md; everything below links there instead of repeating it.
Politrack tracks Austrian politicians from EU to municipal level (roles, votes, money, statements,
meetings, social media, legal proceedings). Every fact is a claim backed by evidence links. Backend:
Python (FastAPI, Postgres 16 + pgvector, Procrastinate workers, MCP server). Frontend: Next.js 15 with
next-intl, German by default, English at /en. Deployed with Docker Compose on the owner's Unraid server.
Before you start
| Task | Read |
|---|---|
| Pick up open work | docs/roadmap.md: Next, Not yet verified, Known limitations |
| Add a data source | docs/contributing/adapters.md, legal checklist in §0 first |
| Derive facts from stored data | docs/contributing/enrichers.md |
| API endpoint or MCP tool | docs/contributing/api.md |
| UI page or new claim type in the UI | docs/contributing/frontend.md |
| How the UI should look and read | docs/design/README.md, design-system.md |
| Tests and fixtures | docs/contributing/testing.md |
| Why it is built this way | docs/architecture/02-comparison-and-decisions.md |
| Tables, claim types, identifier schemes | docs/data-model.md |
| What each source allows | docs/sources.md, docs/legal.md |
Ground rules
- Legal access only. Collect as much as possible, but only through official APIs, open data and
pages whose terms and robots.txt allow it. Never use X proxies (Nitter and the like), paywall
bypasses, logged-in scraping, or WhatsApp/Signal. Telegram: public channels via the
t.me/sweb preview only. A robots.txt exemption needs the publisher's documented permission, recorded in the adapter'sSourceSpec.tos_notes; the RTR API is the only one so far. If a source can't be accessed cleanly, say so and propose legal alternatives instead of working around it. - Every claim has evidence. No claim without a document link.
- Fairness. Neutral wording. Legal matters carry the procedural stage and "Es gilt die
Unschuldsvermutung". Automatically detected legal claims stay in review (
review_status = pending). Connections are not accusations. Personal data only in the person's public role. - Not public yet. The instance is private as of October 2026. The X deletion sync (roadmap) is deferred until launch, but must exist before the instance goes public or an X token is configured.
- Commits: only when the owner asks, as
Simon Diesenreiter <simon@disi.dev>, not the global (work) address:git -c user.email=simon@disi.dev commit …, unless the repo-local config sets it.
Invariants that are easy to break
- Four stages. Adapters fetch only through
Requestobjects handled by the framework'sFetcher(robots, throttling, TDM, archive), never with their own HTTP client.parse()is pure: no database, no network. Only theLoaderwrites claims. Enrichers derive from stored data. - Claim identity. Claims with a facet get their fingerprint from it. Claims without a facet need an
explicit
key, or duplicates and lost updates follow. - Parser changes: bump
parser_version. Otherwise unchanged responses are skipped and the fix never applies.politrack reparse <adapter>rebuilds from the raw archive and deletes claims the new parser no longer emits. - Golden fixtures: new or changed parsers come with a fixture (
politrack fixture …). Review the.expected.jsondiff; don't blindly regenerate withUPDATE_GOLDEN=1. - Schema:
0001_schema.sqland0002_seed.sqlare applied in existing databases. Change the schema only with a new numbered migration, withCOMMENTs on new tables and columns. - API types: after changing a response model, regenerate the frontend types:
cd backend && politrack openapi --ts ../web/src/lib/api-types.ts. - UI text: every new key goes into both
web/messages/de.jsonanden.json.
Commands
cd backend && pytest # unit + golden tests, no DB or network needed
politrack sources list # adapters, enrichers, schedules, queues
politrack run <adapter> --max-requests 20 -p key=val # limit requests when testing against live sources
politrack reparse <adapter> # re-parse archived responses
politrack enrich | status | review | migrate # enrichers, recent runs, legal review, schema
The owner's development machine
These notes apply to the owner's Windows PC (Docker Desktop, Git Bash, PowerShell):
- Git reports "dubious ownership" for this folder. Don't change the global git config; prefix git
commands with
git -c safe.directory=C:/Users/disi0/source/repos/politics-tracking. - PyPI and npm downloads failed there in October 2026 (a network filter blocks
files.pythonhosted.organdregistry.npmjs.org). Until that is fixed, run the tests with the Debian test image (testing.md, "Without PyPI access"). The real images,npm installand the workers can't be built or run there. - Local database:
docker compose up -d db(port 5433, passwords in the gitignored.env, data indata/). It has the schema and sources but no scraped data yet, and no job-queue schema until the realmigrateimage runs. Postgres on a Windows bind mount is slow: the first start takes minutes. DATA_DIR, notAPPDATA: Windows already definesAPPDATA, so the compose file usesDATA_DIR.