Internal Streamlit app for the ODI tech team to monitor the health and quality of data
published by the ~170 OpenActive publishers. Read-only, behind Google SSO, restricted to the
theodi.org workspace.
Data comes from the stewards REST API, which fronts a daily BigQuery batch — every page states the snapshot date rather than implying live data.
uv sync --extra devAgainst the real API, copy .streamlit/secrets.toml.example to
.streamlit/secrets.toml, fill in the Google OIDC client, the API token and — for the
interim admin API — api_style = "admin" with api_token_param = "token", then run:
STEWARDS_ENV=dev STEWARDS_DISABLE_AUTH=true \
uv run streamlit run src/stewards/app.pyEndpoints that a deployment has not implemented yet report themselves as not live on the page that needs them; nothing else on the app is affected. Today that is the fleet summary, the contact queue and every monitor except single-feed stalls.
uv run pytest -q --cov=src/stewards --cov-report=term-missing
uv run ruff check --fix . && uv run ruff format .
uv run mypy src.github/workflows/ci.yml runs on every pull request to main, and on pushes to main:
- Lint —
ruff check,ruff format --check,mypy --strict - Tests —
pytest, with the coverage bars enforced rather than aspirational:= 80% project-wide, and >= 90% across
monitors/,components/andapi/
Dependencies install with uv sync --extra dev --locked, so a pyproject.toml change
committed without a refreshed uv.lock fails the build instead of silently resolving to
something the lockfile does not describe.
.github/workflows/cd.yml runs when a release is published — gh release create v1.2.3 --generate-notes is the deploy, and merging to main on its own changes nothing that is
running. It builds Dockerfile, pushes the image to GHCR tagged with the release name (and
sha-<commit>), and points an Azure App Service web app at it using that app's publish
profile. A prerelease is built but not deployed, and a tag that is not an ancestor of
main is refused, since only main has passed CI.
The container keeps the Google OIDC gate; docker/entrypoint.sh writes Streamlit's
[auth] secrets file from app settings at start-up, so nothing is baked into a layer.
The web app, its GHCR pull credentials, its app settings and the publish profile are set
up once by hand — the full procedure, plus rollback, is docs/deployment.md.
BUILD_BRIEF.md— settled product decisions, page map, API contract, visual languageCLAUDE.md— architecture, hard rules, configuration, testing bar.claude/skills/add-monitor/SKILL.md— how to add the next monitorData Stewards Dashboard.dc.html— the approved UI mockup
Two of the eight monitors are built (single_feed_stall, feed_ingestion_error), plus the
overview, and the cross-monitor contact queue. The rest follow their API endpoints. Runbooks
are published separately from docs/ to GitHub Pages.