Summary
With simulation.trace = True, TraceNode.parameters is empty for almost every yearly-period formula. Monthly formulas (SNAP) are recorded, which hides the problem. Verified on policyengine-core 3.30.2 with policyengine-us 1.808.0.
Cause
Simulation._run_formula (simulation.py:1099) soft-recasts tax_benefit_system.parameters to tracing only when a formula runs:
if self.trace and not isinstance(self.tax_benefit_system.parameters, TracingParameterNodeAtInstant):
self.tax_benefit_system.parameters.trace = True
...
But ParameterNode._get_at_instant (parameter_node.py:216) caches the ParameterNodeAtInstant per instant, and the yearly instant is first requested before any formula runs (defined_for and adds/subtracts evaluation, uprating), while parameters.trace is still False. The cached node is therefore a plain ParameterNodeAtInstant, and every later parameters(period).gov.* read for that instant bypasses TracingParameterNodeAtInstant. Monthly instants are created after the recast, so they trace correctly.
Reproduce
from policyengine_us import Simulation
sim = Simulation(situation={...household with a child, employment income...})
sim.trace = True
sim.calculate("ctc_child_individual_maximum", 2026)
node = next(t for t in sim.tracer.trees if t.name == "ctc_child_individual_maximum")
print([p.name for p in node.parameters]) # [] — expected gov.irs.credits.ctc.amount.base
print(type(sim.tax_benefit_system.parameters("2026-01-01")).__name__) # ParameterNodeAtInstant, not Tracing…
Workaround
Before any calculation:
root = sim.tax_benefit_system.parameters
root.trace, root.tracer, root.branch_name = True, sim.tracer, sim.branch_name
# recursively clear ParameterNode._at_instant_cache on every node
Proposed fix
Do the recast in the Simulation.trace setter (simulation.py:523) rather than lazily in _run_formula, and clear the at-instant caches on the parameter tree when tracing is switched on (and off). _run_formula can keep its check as a safety net.
Context
Found while building a traced parameter → variable dependency map for validation matching in policyengine-app-v2 (PolicyEngine/policyengine-app-v2#1180). Related: #542 covers the second gap (scale/bracket reads never recorded).
Summary
With
simulation.trace = True,TraceNode.parametersis empty for almost every yearly-period formula. Monthly formulas (SNAP) are recorded, which hides the problem. Verified on policyengine-core 3.30.2 with policyengine-us 1.808.0.Cause
Simulation._run_formula(simulation.py:1099) soft-recaststax_benefit_system.parametersto tracing only when a formula runs:But
ParameterNode._get_at_instant(parameter_node.py:216) caches theParameterNodeAtInstantper instant, and the yearly instant is first requested before any formula runs (defined_forandadds/subtractsevaluation, uprating), whileparameters.traceis stillFalse. The cached node is therefore a plainParameterNodeAtInstant, and every laterparameters(period).gov.*read for that instant bypassesTracingParameterNodeAtInstant. Monthly instants are created after the recast, so they trace correctly.Reproduce
Workaround
Before any calculation:
Proposed fix
Do the recast in the
Simulation.tracesetter (simulation.py:523) rather than lazily in_run_formula, and clear the at-instant caches on the parameter tree when tracing is switched on (and off)._run_formulacan keep its check as a safety net.Context
Found while building a traced parameter → variable dependency map for validation matching in policyengine-app-v2 (PolicyEngine/policyengine-app-v2#1180). Related: #542 covers the second gap (scale/bracket reads never recorded).