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.
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.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. Current master
0946ba3d826b9a9bfece08b28008b52a37b554c4, including its copy/pickle repairs, is merged without rewriting history. Independent source review of the exact repaired head found no introduced P1/P2; current-head green CI and downstream impact remain required before landing. #576 is absent from this master; the second lander must connect this PR's newHolder.delete_arrayto #576's_evict_fast_cache, preserving input protection and exact spiral cleanup. #566 is planned to fold into #576. 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.axiom: n/a: core engine repair, no policy encoding.
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.
Generated with Claude Code.