Skip to content

Add a Deployments developer tool: which .py and data build each service serves - #1450

Merged
PavelMakarchuk merged 14 commits into
mainfrom
deployments-dev-tools
Oct 28, 2026
Merged

PavelMakarchuk merged 14 commits into
mainfrom
deployments-dev-tools

Conversation

@PavelMakarchuk

@PavelMakarchuk PavelMakarchuk commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds /{country}/dev-tools/deployments (US and UK), next to API status. For each PolicyEngine service it shows the policyengine.py version, country model and data build the service declares in its lock file, and what the running service serves.

How a service maps to a data build:

  1. The service's lock (uv.lock) on its default branch pins policyengine (.py).
  2. That .py tag's src/policyengine/data/bundle/manifest.json names the certified data build and model version for each country.
  3. The latest .py on PyPI is the reference for "Latest data" / "Older data".

Served versions come from three small runtime reports:

  • api.policyengine.org/v2/tax-benefit-models/by-country/{country} (620 B) gives the served .py. The API runs every US/UK economy simulation on the gateway worker for that exact .py (it refuses dataset overrides), so the page reads the API's data build from that worker in the gateway's /versions. A missing worker shows "No gateway worker" in the data-build cell; a worker that reports another build than its .py certifies shows both builds. If the gateway report cannot be read, the build falls back to the one the served .py's bundle certifies. (The full /{country}/metadata also has data_version, but the US response is ~75 MB with no range support.)
  • The simulation gateway's /versions (14 KB) gives the latest route's .py, model and data build.
  • The household API's /versions gives the country-model version of its current channel (compared with the lock) and frontier channel (both shown by name).

The table has four columns: service, .py, country model and data build. Where a running service serves a different .py, model or data build than its lock on main, the cell lists both values ("main"/"live", or the household API's own "current"/"frontier").

The header shows Latest versions: what the latest .py certifies (.py, country model, Core, data build) next to the newest release of each (PyPI; Microcosm's public releases API for its current data build).

Service Declared (today) Served (today)
PolicyEngine API .py 6.10.0 Same as main; data build from the gateway worker for 6.10.0
App v3 (through the API) .py 6.10.0 Same as main
Simulation API .py 6.10.0 Same as main (15 older .py routes also kept)
Household API policyengine-us 2.53.1 / policyengine-uk 2.88.18, no .py, no microdata US differs from main: current serves 2.42.4; 2.53.1 runs only on frontier. UK same as main.

API v2 alpha is not listed: its repository is archived and v2.api.policyengine.org no longer resolves.

US data is current (populace-us-2024-spm-20260915); UK services serve …20261019T083323Z-6cba5d71, while .py 6.11.1 certifies …20261025T091414Z-a6299504.

This moves PolicyEngine/calibration-diagnostics#271 here: which service runs which version is PolicyEngine infrastructure, not Microcosm calibration. Data build IDs link to their Microcosm calibration pages.

Live since

The table's Live since column shows when the version users get went live, from each service's own report (no GitHub token):

Until those merge and a new .py version is deployed, the API and simulation API show "—". Both PRs record a date only when they observe the change, so they never show a later, wrong date.

Limits

Test plan

  • bun run typecheck, bun run lint, bun run format:check
  • vitest run: 55 files, 501 tests pass (new: __tests__/lib/deployments.test.ts, __tests__/pages/dev-tools-deployments.test.tsx)
  • Local check: /uk/dev-tools/deployments, /us/dev-tools/deployments, the dev-tools card, /api/deployments?country=ca → 404, and 375 px width (no page-level horizontal scroll)

🤖 Generated with Claude Code

…ce serves

/{country}/dev-tools/deployments shows, for the US and the UK, which
policyengine.py version, country model and data build each PolicyEngine
service declares in its lock file, and what the running service serves.

- Declared: each service's lock on its default branch pins .py; that .py
  tag's bundle manifest certifies one data build per country. The latest .py
  on PyPI is the reference.
- Served: api.policyengine.org's /v2/tax-benefit-models/by-country/{country}
  reports the served .py (the full /metadata is ~75 MB for the US), and the
  simulation gateway's /versions reports the latest route's .py, model and
  data build. A service whose served versions differ from its lock is marked
  "Not deployed".

Services: the API, app v3 (through the API), the simulation API, the
household API (no microdata, so no data build) and API v2 alpha. Moved here
from PolicyEngine/calibration-diagnostics#271: this is PolicyEngine
infrastructure, not Microcosm calibration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
policyengine-calculator-next Ready Ready Preview Oct 27, 2026 8:01pm UTC
policyengine-website Ready Ready Preview Oct 27, 2026 8:01pm UTC

Request Review

The seven-column table needed about 1,100 px, so on narrower windows the
data-build column was cut off and every cell wrapped. Each service is now
a card: the service and its links on top, then a Declared and a Served
column with the same labeled rows, stacked on narrow screens. The
latest-bundle summary uses the same labeled rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cards show detail but not drift across services at a glance. An
overview table now lists every service in one row: deployment status,
status against the latest .py, and the .py, model and data-build values.
Where a running service serves something other than its lock, the cell
turns red and shows "lock X / serving Y". Build IDs are shortened to their
date and hash ("2026-10-19 · 6cba5d71"); the full ID is in the tooltip and
in the card. Each service name links to its card under "Details".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the per-service detail cards and the note under the table. The
repository, lock and runtime-report links move under each service name,
and each status label shows its explanation as a tooltip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The latest-bundle box shows .py, model and data build as labeled values
  in one row; the "every reporting service is deployed" label is gone (the
  red "N not deployed" label still appears when a service drifts).
- Overview build IDs drop the repeated producer, country and year prefix:
  "spm · 2026-09-15", "national · 2026-10-19 · 6cba5d71". They wrap only
  between parts, and the table fits a 1024 px window with a drifted row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The latest-bundle box showed the full build ID while the table showed the
short form, so the two could not be compared by eye. The box now uses the
same short form ("national · 2026-10-25 · a6299504", full ID in the
tooltip), the same value size as the table, and a larger heading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The API sends each US and UK economy simulation to the simulation
gateway's worker for its own .py. The page now looks up that worker in
the gateway's /versions instead of inferring the data build from the .py
bundle:

- the worker exists and reports the certified build: the API's data build
  is reported, not inferred;
- no worker for the API's .py: "No worker" (economy simulations fail);
- the worker reports another build than its .py certifies: "Not deployed",
  with both builds named.

App v3 inherits the API's report, so it gets the same check. When the
gateway report cannot be read, the API falls back to the .py bundle as
before. The header counts every service that needs attention.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Household API: its /versions lists a `current` channel (what users get)
  and a `frontier` channel (the next rollout). The page compares `current`
  with the lock and names `frontier` in the explanation. Today it shows
  real drift: current serves policyengine-us 2.42.4 while the lock declares
  2.53.1, which runs only on frontier.
- API v2 alpha: the repository is archived and v2.api.policyengine.org no
  longer resolves, so the service is removed from the list instead of
  showing a lock file for something that does not run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A service whose running version differs from its lock is still deployed
and serving; only the version differs. The column is now "Live vs main"
with "Same as main" / "Differs from main", drift cells read "main X /
live Y", and explanations say "the lock on main declares".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rift

- The header is now "Latest versions" with two aligned rows: what the
  latest .py certifies (.py, country model, Core, data build) and the
  newest release of each (PyPI for .py, the country model and Core;
  Microcosm's public releases API for its current data build).
- The household API's model cell uses its own channel names, "current" and
  "frontier", when either differs from main.
- Drift cells keep the table's font and size, label each line in grey and
  colour only the differing value; no red cell background. The
  "N services need attention" label is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overview keeps Service, .py, country model and data build. Where the
running service differs from main, a cell lists the values as labeled
lines ("main"/"live", or the household API's "current"/"frontier"); labels
and values now share the table's monospace font and baseline, without red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each Microcosm data build in the overview links to its release page on the
Microcosm calibration dashboard, like the header already does. The
service cell keeps its repository link and drops the "lock" and "report"
links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The household API reports `deployed_at` for each channel in /versions; the
table now shows when its `current` channel (what users get) went live.
Services that report no deploy time show a dash until they do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The API's GET /deployment (PolicyEngine/policyengine-api#4084) reports
policyengine_version_live_since, and the gateway's /versions
(PolicyEngine/policyengine-sim-api#961) reports latest_since. The page
reads both when present; an API without the route, or a gateway without
the field, shows no date as before. A missing gateway worker keeps the
API's own date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PavelMakarchuk
PavelMakarchuk marked this pull request as ready for review October 27, 2026 20:11
@PavelMakarchuk
PavelMakarchuk merged commit e96c22d into main Oct 28, 2026
10 checks passed

This branch was successfully deployed

2 active deployments
Preview – policyengine-website — 419f4c0b Deployed Oct 27, 2026 by vercel[bot]
Preview – policyengine-calculator-next — 419f4c0b Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant