Skip to content

Upgrade engine to 0.47 and observability to 1.3 as exact pins; drop unused pydantic-ai#175

Merged
anth-volk merged 5 commits into
mainfrom
fix/compiled-0.47-migration
Jul 3, 2026
Merged

Upgrade engine to 0.47 and observability to 1.3 as exact pins; drop unused pydantic-ai#175
anth-volk merged 5 commits into
mainfrom
fix/compiled-0.47-migration

Conversation

@anth-volk

@anth-volk anth-volk commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #174
Fixes #173
Fixes #170

What

Migrates both capped dependencies to their current upstream versions, switches them to exact == pins, and removes the one dependency that blocked the resolve.

policyengine-uk-compiled >=0.44.0,<0.45==0.47.0

The one breaking change in between: since 0.45, Simulation requires an explicit data source — data-less Simulation(year=...) raises ValueError.

  • Parameter-lookup call sites (backend/tools/dispatch.py ×2, modal_app.py _preload_engine): pass dataset="frs". Construction is lazy and get_baseline_params() shells out to the binary, so no survey data loads on these paths. Can be simplified when Expose dataset-free baseline parameter export policyengine-uk-v2#112 (dataset-free parameter export) ships.
  • Sandbox contract preserved (backend/engine/sandbox.py): SafeSimulation injects dataset="frs" when model-written code passes no data source — the run_python tool description and tool-contract eval cases document data-less construction, and the FRS guard bookkeeping already treated missing dataset as "frs". test_rejects_frs_row_level_microdata doubles as the regression test.
  • Also lands with the jump: fiscal-year 2030/31 parameters, realterms module, DWP cost-of-living payments (2022/23–2023/24), mortgage interest in AHC, region-level LHA rates, local-authority EFRS, dataset version pinning. Baseline results shift for affected years/measures; no output-contract changes.

policyengine-observability >=1.0.0,<1.1==1.3.1

Exact pins + updater format change

Range pins let a new in-range release reach production on the next image build with no PR and no tests — the silent-drift failure class behind #167/#168. With == pins, every version change arrives as a tested PR via the dependency-update workflow. dependency_bump.py is updated in lockstep (same PR, so main never has a format the updater can't parse):

  • same-minor releases → patch bump PR, auto-merge on green (was: in-cap floor bump);
  • releases crossing a minor → bump PR left for review (was: cap widening);
  • discovery still proposes the newest same-minor patch even when a newer cross-minor release exists.

Unit tests updated for the new format (20 cases); a live DRY_RUN=1 sweep against PyPI reports both pins current. This is a step toward #168 (full dependency locking) for the two first-party packages; transitive deps still float.

Removed: pydantic-ai[anthropic]

Nothing in the backend imports it (its only reference was a vestigial importorskip in a test whose imports don't need it), and docs/engineering/skills/uk-chat-runtime.md already states Pydantic AI is not the active orchestration layer. It also hard-blocked this upgrade: pydantic-ai pulls logfire, whose opentelemetry-sdk<1.43.0 cap conflicts with observability 1.3.1's >=1.43.0 floor, sending pip into deep backtracking (locally ending in a failed temporalio source build). Dropping it removes a large transitive tree (logfire, temporalio, xai-sdk, cohere, mistralai) from the production image. The anthropic SDK is a direct requirement and is unaffected.

Verification

Fresh Python 3.13 venv resolved from this branch's backend/requirements.txt (compiled 0.47.0, observability 1.3.1, otel-instrumentation-fastapi 0.64b0 — no resolver backtracking): 227 passed, 10 skipped, including compiled-integration tests, sandbox FRS-guard tests, the CORS preflight tests against the instrumented app, and the updated dependency-bump script tests. As a bonus validation of the original bug: an intermediate env with instrumentation enabled but the old 0.63b1 instrumentor reproduced the preflight 500s exactly.

Notes

  • Once merged, the dependency-update workflow's pending proposals for both packages become no-ops; if the daily sweep opened chore: bump ... PRs in the old range format in the meantime, close them in favour of this one.
  • Route observability logs to Google Cloud Logging #142 should be rebased on this.

🤖 Generated with Claude Code

Since 0.45 the engine requires an explicit data source: data-less
Simulation(year=...) raises ValueError. Pass dataset="frs" at the
parameter-lookup call sites (construction is lazy and
get_baseline_params() shells out to the binary, so no survey data
loads), and inject the same default in the sandbox wrapper so
model-written run_python code keeps its documented contract.

Backend suite against 0.47.0: 229 passed, 10 skipped.

Fixes #174

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
policyengine-uk-chat Ready Ready Preview, Comment Jul 3, 2026 7:27pm

Request Review

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Beta preview has been cleaned up because this PR was closed.

anth-volk and others added 2 commits July 3, 2026 20:48
Nothing in the backend imports pydantic_ai — its only reference was a
vestigial pytest.importorskip guarding a test whose imports don't need
it, and docs/engineering/skills/uk-chat-runtime.md already states
Pydantic AI is not the active orchestration layer. It also blocked the
policyengine-observability upgrade: pydantic-ai pulls logfire, whose
opentelemetry-sdk<1.43.0 cap conflicts with observability 1.3.1's
>=1.43.0 floor, sending pip into deep backtracking. Dropping it also
removes a large transitive tree (logfire, temporalio, xai-sdk, cohere,
mistralai) from the production image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…umentation

Since 1.1, structured logs route through a severity-aware destination
manager: handled-error operation logs emit at WARNING instead of INFO.
Update the one log-capture test that asserts an operation_failed record
to capture the WARNING level.

1.3.1 forward-pins the OTel stack (instrumentation >=0.64b0), fixing the
_IncludedRouter AttributeError that 500'd CORS preflights on FastAPI
>=0.137 (#167), so remove the instrument_fastapi=False workaround from
#169. The preflight regression test now exercises the instrumented app;
the instrumentation config test flips to assert it is enabled.

Backend suite against compiled 0.47.0 + observability 1.3.1 in a fresh
resolve: 229 passed, 10 skipped.

Fixes #173
Fixes #170

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@anth-volk anth-volk changed the title Upgrade policyengine-uk-compiled to 0.47 with explicit dataset selection Upgrade engine to 0.47 and observability to 1.3; drop unused pydantic-ai Jul 3, 2026
…pins

Range pins let a new in-range release reach production on the next
image build with no PR and no tests — the silent-drift failure class
from #167/#168. Exact pins make every version change arrive as a tested
PR through the dependency-update workflow.

The updater keeps its two-tier policy, now keyed on the pinned minor
instead of a range cap: same-minor releases are patch updates
(auto-merge on green); releases crossing a minor are left for review.
Discovery still proposes the newest same-minor patch even when a newer
cross-minor release exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@anth-volk anth-volk changed the title Upgrade engine to 0.47 and observability to 1.3; drop unused pydantic-ai Upgrade engine to 0.47 and observability to 1.3 as exact pins; drop unused pydantic-ai Jul 3, 2026
@anth-volk anth-volk force-pushed the fix/compiled-0.47-migration branch from 08a6343 to 2488746 Compare July 3, 2026 19:17
Add engine/constants.py with FRS_DATASET and use it for the parameter
lookup data source, aggregate simulation defaults, the dataset schema
enum/default, the dataset label map, and the sandbox row-level guards.
engine/ is imported by tools/ and never the reverse, so the constant is
reachable everywhere without cycles.

modal_app.py keeps its literal: _preload_engine runs at Modal image
build before backend/ is added to the image, so it cannot import
backend modules. Test literals stay as-is — they assert the concrete
wire contract rather than restating the constant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@anth-volk anth-volk marked this pull request as ready for review July 3, 2026 19:28
@anth-volk anth-volk merged commit 0c3ddf9 into main Jul 3, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant