Repository navigation
Conversation
max_spiral_loops capped how many uncached frames of one variable the stack could hold, returning the default where the cap fell. Since it counted uncached frames only, a recursion's result depended on what had been calculated before: with an input of 1 in 2010 and recursive(t) = recursive(t - 1) + 1, 2021 was 10 in a new simulation and 12 after calculating 2020. policyengine-us weeks_worked (formula_2025 reading the year before, input in 2024) is cut this way from 2035 on. A recursion that reaches the limit while heading towards an input, or a period with no formula, now unwinds to the outermost calculate, which calculates the deepest period first and starts again; the chain is evaluated all the way down, max_spiral_loops frames at a time. One with no anchor is still cut, but no value calculated while a cut is on the stack outlives that calculation (values above a cut used to be kept), spiral purges delete exactly the period under the calculating branch's name (they deleted contained periods under the default branch), and each clone has its own invalidated set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
Conflicts: - holders/holder.py (put_in_cache): kept master's derived guard (a derived value never replaces a readable input) and derived=True storage, then the PR's _note_cached_value call after the store. - simulations/simulation.py (clone): kept master's storage-directory and _user_input_keys copying, then the PR's per-clone invalidated_caches. Semantic integration: delete_exact now uses master's bookkeeping. On memory, _stop_sharing (master's _shared is a frozenset while nothing is shared, so .discard raised AttributeError) and _unmark_input; on disk, the _derived mark is discarded with the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Branch and copy cleanup (findings 1 and 4). A cut in a simulation's calculation no longer deletes its branch-name key from every descendant, which deleted a descendant's own input stored under the same name. A copy (clone or branch) instead drops, when it is made, the values the simulation it copies is waiting to purge: every such value is marked by the time it is cached, and a copy never sees what is cached after. Purges delete only values the simulation calculated (derived), never an input. Swallowed requests (2). Each request to finish a deeper period first is recorded as outstanding when raised. A formula that returns while one is outstanding (a bare except caught it) has its value dropped and the request raised again; an error raised while one is outstanding is treated as the request. requires_computation_after (3). A deferred period keeps the names of the variables that needed it, so its caller still counts while it is calculated with an empty stack. Tracing and storage (6, 7). The flat trace prefers a completed node to an abandoned one. Deferred values are kept for the outermost calculation in its own cache, which _calculate reads, so deferral works the same traced or not and when the holder stores nothing; values calculated under a cut are marked for purging when written to the fast cache too. Termination (8). MAX_SPIRAL_DEFERRALS is gone: it cut long chains depending on what was cached. Each deferred period starts at most once per outermost calculation, and only on the requested simulation or its registered branches (a copy made in a formula is made anew on every retry), so the calculation still ends. Finding 5 is settled by master's #562 (only inputs carry over); its regression test passes on the merge commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep master clone aliases, system entities, independent baselines, and explicit input/contained-period invalidations. Distinguish spiral cleanup from explicit invalidation, and prefer accepted trace results over provisional cut retries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Retain the original recurrence order generator. Compare finite anchored systems against an independent iterative NumPy evaluator across caller authorization, bare exception handlers, tracing, cache policies, copies and descendant inputs. Compare exact-deletion snapshots and provenance across copied memory, shared memory and disk storage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describe the shared behavior without claiming disk delete uses contained-period deletion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use readable storage provenance instead of stale input registry keys. Preserve replay bookkeeping and inherited inputs. Add traced and untraced deletion regressions and a bounded provenance property; clarify deferred-key retry invariants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve the canonical docs release job without changing scheduler, holder, storage or tracer behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Retain all example counts and semantic assertions. Suppress only the slow-generation health check for the bounded two-variable generator after a 47-second draw interrupted otherwise passing properties on the shared host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve the incoming Population protocol guards, EnumArray serialization, release artifacts, and focused copy coverage alongside deferred recursion scheduling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 9, 2026
This branch has not been deployed
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.
Finite anchored period recursions are evaluated in full regardless of previously calculated periods, tracing, or persistent cache policy. Unanchored recursions still use the existing depth limit, and values reached by a cut are discarded when the calculation ends.
Repair and invariants
requires_computation_after._do_not_store, and cache blacklisting.Round-2 findings
All eight review findings are addressed: descendant input deletion; swallowed scheduling exceptions; lost prerequisite caller; invalid clone snapshots; carry-over from calculated values (settled by master's input-only provenance);
Noneretry traces; tracing/cache-policy differences; and cache-dependent deferral exhaustion. The inherited memory sharing, input/derived metadata, disk directory lifetime, clone aliases, system entities and reform baselines from master are preserved.The original 200-example recurrence order property is retained. Added differential properties compare finite forward/backward recurrences with an independent iterative NumPy evaluator across tracing, storage policies, guarded formulas and caller scope; additional properties cover cut copies, descendant inputs and deleted input provenance. Exact deletion is compared across copied memory, shared memory and disk snapshots.
Previous validation
Targeted, serial single-file checks: 245 passed in total.
tests/core/test_spiral_order.pytest_spiral_order_property.pytest_spiral_scheduler_property.pytest_spiral_storage.pytest_spiral_trace_retries.pytest_cycles.pytest_fast_cache_guards.pytest_clone_invalidated_caches.pytest_carry_over_order.pytest_docs_deployment.pytest_simulation_copy_pickle.pytest_simulation_copy_pickle_property.pyThe final merge leaves the repaired scheduler/storage/tracer source and spiral tests unchanged. The eight deleted-input cases also passed again through the offline
uv runlauncher before the final clean master merge. Ruff format/check passed for all 19 repaired or incoming Python files, andgit diff --checkpassed. Final copy checks emitted only the existing NumPy generic-timedelta deprecation warning.Previous repaired head:
f34b54c569c6d700ca18046e8ff106c0379ce5e3.Baseline checks on the history-preserving merge
2ed6c34db0f5e6eae1b9dee6397c5c034938bb93reproduced 32 failures, with 42 passing and 27 deselected cases. Carry-over cases passed there because master's input-only provenance already repairs them. The additional deleted-input regression failed in all eight cases before the provenance fix.The fresh dependency install failed on PyPI DNS. Successful checks use the existing read-only Python 3.13 environment, with this checkout first on
PYTHONPATH; the final focused provenance and copy checks launch it through offline, frozen, no-syncuv run. The first run of the added recurrence differential file hit Hypothesis's input-generation timing health check (one failed, three passed); onlytoo_slowwas suppressed for its two bounded generators, with every example count and assertion retained, and all four tests then passed. No full suite, country tests, partner baselines, documentation build or new microsimulation was run. Earlier full-suite and impact results are historical evidence only.Composition and status
This remains draft and held for Max's core-semantics merge decision. Master
7399c4ed8b9401febd546512cc55ba1c3e896ab0is merged without rewriting history; the current merge and validation are recorded below. #576 is included, and this PR's exactHolder.delete_arraynow calls its_evict_fast_cachewhile preserving input protection. Current-head green CI and downstream impact remain required before landing. No decision ID is associated with this bounded repair.Conditional formula-only base cases and
defined_formasks still use the existing cut behavior. Mixed-direction finite recurrence graphs have less differential coverage than same-direction anchored graphs; their existing regressions and the original order property are preserved.Historical downstream evidence
The following measurements belong to the original PR head and its original scratch merge. They have not been repeated on
f34b54c569c6d700ca18046e8ff106c0379ce5e3; current-head impact and partner gates remain pending.Real runs on master b78b0ba and on all three of #571/#572/#573 merged together (scratch merge 37aadada), each output compared array by array (
compare.py). The PE-US 2026 run was also repeated on each branch alone, and the 2035 run on #572 alone; all identical too.A probe of the PE-US 2026 and 2035 and PE-UK 2026 and 2042 runs on master found one recursion cut by the limit: PE-US
weeks_workedin 2035 (below).PE-US
weeks_worked(formula_2025: last year's value) is the one recursion the probe saw reach the limit on real data: a new 2035 simulation cuts it at 2025. The eCPS file carries noweeks_worked, so every year is the default 0 and the cut changes nothing above. Where the input exists it does. A real policyengine-us run on a household with one worker (40 weeks entered for 2024):weeks_worked,spm_unit_work_expensesSo household calculations from 2035 on (ten years after the 2025 formula start) change, to the value calculating year by year gives.
Merge with master
Merged master
7399c4ed8b9401febd546512cc55ba1c3e896ab0into the confirmed PR headf34b54c569c6d700ca18046e8ff106c0379ce5e3with merge commite5455b8a306ca71e8795f1d443a17ed73c207923. Current head:7bc9a284efd96915ca1242e5e84d2dcd7e573722. The branch history is preserved and this PR remains draft for Max's core-semantics ruling.Conflict resolutions (two source files, six conflict blocks):
simulation.pyimports: retain the scheduler's dataclassfield, and master'sLockandTupleimports.simulation.pycalculation cleanup: retain master's store-history propagation and calculation-frame/counter teardown, then end the trace, release the scheduler's cut frame, and purge invalid results. Deferred exceptions unwind both sets of state.simulation.pyformula execution: retain master's formula-simulation context token/reset and the PR's outstanding-deferral check after the formula returns. A formula cannot swallow a scheduling request and publish its fallback value.simulation.pyfast cache: keep master's input-unchanged guard and the PR's cut-result marking inside that guard, so obsolete input-dependent results are not cached and cut-derived cached results are purged.simulation.pyclone setup: retain master's independent store history/requested-variable state and reset its active frames/input contexts before dropping the PR's copied cut values. Reform baselines, aliases and system entities retain master's clone behavior.holder.pycache writes: keep master's_set(..., is_input=False, derived=derived)and store-history recording, together with the PR's_note_cached_value. Calculated writes retain provenance and are marked for exact cleanup when reached by a cut.Non-textual interactions and fixes:
37847869045was read withgh run view --log-failed. On the merged code, the unchangedtest_every_clone_has_a_baseline_of_its_ownreproduced the exactset() == {('income_tax', '2022-01')}failure. This was a PR ownership bug, independent of the depth heuristic: a parent purge consumed a child's explicit invalidation. Commiteac7ec566431f7b5ea9bdffefda10b2048e7ac58restricts descendant cleanup to cut-derived values. The original 60-example property now passes unchanged. Ten deterministic cases cover nested branches, tracing, mixed invalidations, an explicit/cut overlap, and cloning before cleanup. Overlapping cut values are deleted exactly while the explicit marker remains pending.1d8e8e255a21395c93ce8539524a8269e51ecfbband7bc9a284efd96915ca1242e5e84d2dcd7e573722integrate master's input-change retries and ADD/DIVIDE caches. Completed deferred values retain foreign dependencies and mutation versions, are invalidated only for their own/dependent simulations, and reopen only completed keys; pending keys still guard cycles. Mutating inputs use master's existing ten-retry bound; stable anchored scheduling retains no deferral-count limit. Stale deferred frames cannot publish finished values. Unrelated scratch-simulation writes do not consume the retry budget. Direct option results that encountered a cut are returned without storing them, preserving master's routing, dtype, provenance and input-set-meanwhile rules.test_value_a_spiral_defaults_is_trackedinitially failed because it searched for retained cut-derived storage. That storage is deliberately purged by this PR. It now derives the cut period from the recorded dependency history, asserts the cut cache is empty and the dependency is recorded, and retains the original fresh-simulation differential assertion. Its entire file passes (98 tests). No clone-baseline invariant or generated example count was weakened.Validation at the current head: 1737 passed, 2 expected failures, 0 skipped across 91 files, each invoked independently as
uv run pytest -q <file>in the foreground. Every test usedOMP_NUM_THREADS=4 OPENBLAS_NUM_THREADS=4 MKL_NUM_THREADS=4 VECLIB_MAXIMUM_THREADS=4 NUMEXPR_NUM_THREADS=4 OMP_WAIT_POLICY=PASSIVE. The workspace-local environment was installed withuv sync --extra dev; test invocations used frozen/no-sync uv with CPython 3.14.7's free-threading build (h5py enables the GIL). No folder run, full suite, country run or microsimulation was performed.The first 14 integration cases reproduced 12 failures before the cache-integration repair; all 24 final integration cases pass, including changing own/foreign inputs, six successive settling input changes, an unsettled deferred frame, continuously changing inputs, cut ADD/DIVIDE input precedence, and unrelated scratch writes. The 10,002-month cold/warm anchored-chain regression and the original 200-example order property pass.
tests/core/test_apply_reform_invalidates_cache.pytest_apply_reform_preserves_user_inputs.pytest_branch_helper_invalidation.pytest_branch_input_fixed_point.pytest_branch_input_fixed_point_property.pytest_branch_input_invalidation.pytest_branch_input_invalidation_property.pytest_branch_scoped_delete_arrays.pytest_branch_shared_arrays.pytest_branch_shared_arrays_differential.pytest_carry_over_order.pytest_carry_over_order_property.pytest_clone_invalidated_caches.pytest_clone_rebinding.pytest_clone_rebinding_property.pytest_clone_reform_baseline.pytest_clone_reform_baseline_property.pytest_cycles.pytest_data_storage_dir.pytest_data_storage_dir_property.pytest_dataset_default_role.pytest_dataset_default_role_property.pytest_disk_storage_differential.pytest_disk_storage_ownership.pytest_dump_restore.pytest_fast_cache.pytest_fast_cache_contained_periods.pytest_fast_cache_contained_periods_property.pytest_fast_cache_guards.pytest_holder_branch_fallback.pytest_holder_write_fast_cache.pytest_holder_write_fast_cache_property.pytest_holders.pytest_input_tier_invalidation.pytest_join_with_persons_identity.pytest_join_with_persons_identity_property.pytest_macro_cache_not_singleton.pytest_on_disk_storage_clones.pytest_opt_out_cache.pytest_option_result_cache.pytest_option_result_cache_property.pytest_reform_baseline_system.pytest_registered_input_deletion.pytest_restore_input_provenance.pytest_restore_input_registry.pytest_restore_input_registry_property.pytest_set_input_helper_order.pytest_set_input_helper_order_property.pytest_simulation_builder.pytest_simulations.pytest_spiral_merge_integration.pytest_spiral_order.pytest_spiral_order_property.pytest_spiral_scheduler_property.pytest_spiral_storage.pytest_spiral_trace_retries.pytest_stale_frame_cache.pytest_storage_anchored_periods.pytest_storage_delete_branch.pytest_storage_input_index.pytest_storage_shared_index.pytest_storage_shared_index_differential.pytest_subsample_baseline_branch.pytest_subsample_computed_values.pytest_subsample_computed_values_property.pytest_subsample_deleted_inputs.pytest_subsample_entity_weight_totals.pytest_subsample_inputs_only.pytest_subsample_inputs_only_property.pytest_subsample_invalidates_cache.pytest_subsample_membership_ids.pytest_subsample_restore_inputs.pytest_subsample_sampling_period.pytest_subsample_sampling_period_property.pytest_subsample_structural_ids.pytest_subsample_weight_fallback.pytest_tax_benefit_system_cache_identity.pytest_tracers.pytest_tracing_parameter_isolation.pytest_tracing_parameter_isolation_properties.pytest_uprating_order.pytest_uprating_order_property.pytest_user_input_keys.pytest_user_input_keys_property.pytest_user_input_keys_soundness.pyvariables/test_non_numeric_uprating_and_defined_for.pytest_simulation_copy_pickle.pytest_simulation_copy_pickle_property.pytest_option_result_input_provenance.pytest_disk_period_filenames.pysimulations/test_formula_randomness_concurrency.pyRuff on all 91 Python files changed by the PR or incoming master:
ruff check→All checks passed!;ruff format --check→91 files already formatted.git diff --checkpassed. Documentation review updated the storage/cleanup contract and retained the tracer retry documentation; no documentation build was run.The old body is saved as untracked evidence in
hub-evidence/core572-body-before.md; before/after failure logs, the fix diff and individual-file results are retained there too. The PE-US A/B gate and impact will be rerun by the hub at this merged head. Current-head CI and Max's core-semantics decision remain pending; no review was requested and no PR comment was posted.axiom: n/a: core engine repair, no policy encoding.
Generated with Claude Code.