Repository navigation
Conversation
…ontext Add a typed outcome for a report coordinator that returns its result to the caller instead of writing Stage 12 rows: - ReportExecutionResult carries the aggregate inline, validated as SingleYearMacroOutput, with its canonical JSON SHA-256, the output plan that declares the artifacts' columns, the baseline and reform simulation artifact descriptors, and provenance (bundle, worker version, Modal environment, application, simulation function, version manifest digest and coordinator call ID). Its validator rejects artifacts that do not form the report and a digest that does not match the aggregate. - ReportExecutionFailure and ReportExecutionError carry the redacted error code and summary a Stage 12 row would record. The error pickles with its failure, so Modal can raise it in the caller. - Stage12InvocationContext gains result_delivery. It defaults to stage12_database, so existing callers keep the Stage 12 behavior; a return_to_caller context cannot request a production comparison. The Stage 12 coordinator leaves the new field out of the context it sends to its child simulations, so the child payload keeps exactly the fields it had before and a worker deployed without the field still accepts it. Nothing else reads the field yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
added this pull request to stack #838
October 19, 2026 21:51
2 tasks done
…put plan ReportExecutionResult carries the requested aggregates twice: in requested_aggregates and in output_plan.requirements.aggregates, which the coordinator builds from the same report. The validator accepted a result in which the two disagreed, so a caller storing requested_aggregates could keep a value the output plan contradicts. The validator now requires them to be equal, in the same order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #834
Same-Repository Draft PR Check
PolicyEngine/policyengine-sim-api, not a personal fork.Summary
First of two stacked PRs that let the v2 Modal report coordinator return a typed result to its caller instead of writing Stage 12 rows (API v2 migration, Stage 14 economy reports). This PR adds the types and the selecting field. #837, stacked on it, adds the return mode that uses them.
ReportExecutionResult(newpolicyengine_simulation_contract.report_execution): the result a coordinator returns when the report succeeds.aggregate: the report's aggregate inline, validated as the existingSingleYearMacroOutput.aggregate_sha256: SHA-256 of the aggregate's compact, key-sorted JSON (report_aggregate_sha256). The digest is the same before and after a JSON round trip, and NaN or infinity is rejected.output_plan: theStage12OutputPlanthat declares the columns of the simulation artifacts.baselineandreform: the existingSimulationArtifactDescriptorfor each Parquet artifact (storage URI, media type, checksum, size, row identity, bundle, SPM receipt).provenance: the PolicyEngine.py bundle, worker version, Modal environment, application and simulation function, version manifest digest, and the coordinator's Modal call ID.ReportExecutionFailureandReportExecutionError: the redacted error code and summary that a Stage 12 row would record for the same failure. The error's message is fixed. It pickles with its failure, so Modal'sFunctionCall.getcan raise it in the caller as the same type.Stage12InvocationContext.result_delivery:stage12_database(the default, today's behavior) orreturn_to_caller. Areturn_to_callercontext cannot name a production comparison job, because the comparison is recorded on the Stage 12 report row.result_deliveryout of the context it sends to its baseline and reform workers. The worker payload has exactly the fields it had before, so a worker deployed without the field still accepts it.Apart from the Stage 12 coordinator, which reads it only to leave it out of the context it sends to its workers, nothing reads
result_deliveryin this PR.What a merge deploys
Merging to
mainruns the complete staging and then production deployment, as every merge does: the existing simulation stack, the entrypoint Cloud Run service and the versioned v2 Modal application. The contract library is installed in all of them. Behavior does not change: the entrypoint does not sendresult_delivery, so every context defaults tostage12_database, and the payloads the coordinator sends to its workers are unchanged.Environment variables
This PR adds, removes or rewires no environment variable, workflow input, GitHub secret, Secret Manager secret or Modal secret.
Coordination with other PRs
stage12_execution.pyand the Stage 12 coordinator, and moves coordination tests into a new test module. Whichever merges second will need a rebase. Refactor within ruff's complexity limits, then select every ruff rule (ALL) #804's full rule set reports findings in this code (for example, too many arguments) that it reports in the surrounding Stage 12 code too.SingleYearMacroOutput.ReportExecutionResultvalidates against whatever that model defines, so it needs no change.Testing
libs/policyengine-simulation-contract/tests/test_report_execution.py(26 tests): the default andreturn_to_callerdelivery, rejection of a production comparison and of an unknown delivery, JSON round trip, aggregate validation againstSingleYearMacroOutput, digest mismatch and non-finite values, digest stability (key order, SPM provenance aliases), each artifact consistency check, timezone and unique-aggregate checks, requested aggregates that must match the output plan, unknown fields, failure field bounds, and the error surviving pickling.test_stage12_children_receive_the_context_without_a_delivery_fieldin the executor'stest_stage12_runtime.py: Stage 12 workers receive the same context fields as before.ruff check .andruff format --check projects/*/src libs/*/srcwith the pinned ruff 0.15.22 pass.scripts/run_quality_guards.pypasses.🤖 Generated with Claude Code