Skip to content

Relay simulation run stage and progress on computing economy responses - #3916

Merged
anth-volk merged 3 commits into
masterfrom
feat/relay-simulation-run-progress
Oct 15, 2026
Merged

anth-volk merged 3 commits into
masterfrom
feat/relay-simulation-run-progress

Conversation

@PavelMakarchuk

@PavelMakarchuk PavelMakarchuk commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

While an annual economy run is computing, GET /<country_id>/economy/<policy_id>/over/<baseline_policy_id> now relays the optional stage fields that the simulation entrypoint reports for the job (PolicyEngine/policyengine-sim-api#781). Clients can show real progress instead of elapsed time divided by a fixed duration.

Related: #2001. That issue was deferred to the API v2 calculation migration, with the note that progress should come from coarse, monotonic calculation stages on an asynchronous path. policyengine-sim-api#781 adds those stages to the simulation entrypoint's job status. This PR only relays them for the asynchronous economy route. The synchronous household endpoint is unchanged and stays indeterminate.

Response

A computing body on a worker that reports stages:

{
  "status": "computing",
  "message": null,
  "result": null,
  "stage": "reform",
  "progress": 67,
  "progress_max": 96,
  "submitted_at": "2026-10-13T12:00:00Z",
  "started_at": "2026-10-13T12:00:30Z",
  "stage_started_at": "2026-10-13T12:01:00Z"
}

A segmented US national run also returns "segments": {"completed": 19, "total": 20}.

Unchanged bodies:

  • completed and error responses;
  • computing responses before policyengine-sim-api#781 deploys.

Once #781's gateway deploys, it reports submitted_at for every new job. Jobs routed to worker apps without stage reporting therefore gain that one field and no others. This is additive, and a test pins it.

Changes

  • libs/simulation_entrypoint.py: SimulationRunProgress reads stage, progress, progress_max, submitted_at, started_at, stage_started_at and segments from GET /jobs/{job_id}. The timestamps are AwareDatetime. ModalSimulationExecution.run_progress holds it. Malformed values give None and never fail a poll. stage is a plain string, because later entrypoint versions may add stages.
  • services/economy_service.py: EconomicImpactResult.run_progress is SimulationRunProgress | None and comes straight from the running branch of _handle_execution_state. It is not part of to_dict(), so existing callers see no change.
  • routes/economy_routes.py: calls run_progress.to_dict() and merges the fields only when the status is computing and the fields are present.
  • openapi_spec.yaml: documents the optional fields on the annual route.
  • changelog.d: added fragment.

The completion fields (finished_at, stage_durations) are not relayed: completed results come from the reform_impact table, not from a live poll.

Migration notes

  • Route group: economy, annual route only. It stays on the Flask/API v1 path.
  • Contracts: no migration registry or workflow contract changes. scripts/export_migration_contracts.py produced no diff, and the stable response fields are unchanged.
  • User-visible change: additive optional fields on computing bodies only.

Merge order

Merge after policyengine-sim-api#781 is merged and verified on beta. This PR changes no response until then.

Testing

  • pytest tests/unit tests/contract: 2,391 passed after merging current master.
  • New tests cover reading the fields, missing and malformed fields, an unknown stage value, the relay in _handle_execution_state, and the route bodies (computing with and without fields, completed results).
  • python scripts/run_quality_guards.py: passed.
  • ruff format --check . and ruff check on the changed files: passed.
  • Not run: integration and live tests.

Downstream

policyengine-app-v2 can read progress and progress_max in SocietyWideCalcStrategy: move its time-based estimate between the two values, never backwards, and name the stage in the RunProgress caption.

🤖 Generated with Claude Code

While an annual economy run is computing, the economy route now relays
the optional stage fields that the simulation entrypoint reports for
the job (PolicyEngine/policyengine-sim-api#781): stage, progress,
progress_max, submitted_at, started_at, stage_started_at and segments.

Malformed fields are dropped and never fail a poll. Completed, error
and field-less computing bodies keep their existing shape.

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

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.62%. Comparing base (172bbed) to head (1517a71).
⚠️ Report is 56 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #3916       +/-   ##
===========================================
+ Coverage   45.27%   87.62%   +42.35%     
===========================================
  Files         183      198       +15     
  Lines       11084    12248     +1164     
  Branches     1907     2163      +256     
===========================================
+ Hits         5018    10732     +5714     
+ Misses       5644      920     -4724     
- Partials      422      596      +174     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@anth-volk anth-volk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two typing comments and one mismatch between the description and the response bodies, from a review against PolicyEngine/policyengine-sim-api#781.

Comment thread policyengine_api/services/economy_service.py Outdated
Comment thread policyengine_api/libs/simulation_entrypoint.py Outdated
Comment thread policyengine_api/libs/simulation_entrypoint.py
PavelMakarchuk and others added 2 commits October 15, 2026 09:00
…on-run-progress

# Conflicts:
#	policyengine_api/libs/simulation_entrypoint.py
#	policyengine_api/services/economy_service.py
Review follow-up:
- EconomicImpactResult.run_progress is now SimulationRunProgress | None,
  and the route calls to_dict() once. The service passes
  execution.run_progress through without getattr or isinstance, so a
  wrong type fails loudly instead of dropping the fields. Test doubles
  for computing results now carry run_progress.
- submitted_at, started_at and stage_started_at are AwareDatetime, so a
  malformed timestamp drops the stage fields; they still serialise as
  ISO 8601.
- A test pins the body for a job whose worker reports no stages: once
  policyengine-sim-api#781 deploys, it still gains submitted_at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anth-volk
anth-volk marked this pull request as ready for review October 15, 2026 19:02
@anth-volk
anth-volk merged commit c04b1a5 into master Oct 15, 2026
15 checks passed
@anth-volk
anth-volk deleted the feat/relay-simulation-run-progress branch October 15, 2026 19:04

This branch was successfully deployed

1 active deployment
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.

2 participants