Summary
A PerElement link-score partial that reads the live source through a DYNAMIC index -- e.g. growth[Region] = pop[Region, idx] with idx a time-varying aux -- lowers the frozen occurrence to PREVIOUS(pop[region·r, PREVIOUS(idx)]). The nested PREVIOUS(idx) is rewritten through a synthesized capture-helper aux (the builtins_visitor.rs make_temp_arg machinery), and that helper reads an uninitialized value at the FIRST live step, poisoning that one step's link score to NaN. The observed series is 0, NaN, 0.4974..., ... -- the NaN is isolated to the single step (the score equation recomputes each step), so it degrades one sample rather than the whole series, but it is a spurious NaN in a compiled link score that consumers (relative-score normalization, discovery ranking, FFI surfaces) will ingest.
Where it is pinned
The behavior is deliberately test-pinned as HEAD-preserved: per_element_dynamic_index_scores_preserve_head_lag in src/simlin-engine/src/db/ltm_char_tests.rs asserts results[1] IS NaN, because the NaN is the exact discriminator that the dynamic-index lag is present at all (an un-lagged regression makes the same step finite). The test's comment names this as a pre-existing latent bug tracked separately -- this issue is that tracking. When fixed, that guard must be updated in the same change (the discriminator should become: helper initialized AND lag present).
Fix direction
The synthesized PREVIOUS(idx) capture helper needs a well-defined value at the first live step -- e.g. participate in the initials phase so its first read returns idx's initial value (matching PREVIOUS's documented first-step semantics for direct scalar args, which read the prev_values snapshot seeded from initials), rather than an uninitialized slot.
Components affected
src/simlin-engine/src/builtins_visitor.rs -- the PREVIOUS helper-aux synthesis (make_temp_arg / previous_needs_temp_arg) and/or its initials-phase participation
src/simlin-engine/src/ltm_augment.rs -- the dynamic-index per-element lowering that emits the nested PREVIOUS(idx) form
src/simlin-engine/src/db/ltm_char_tests.rs -- per_element_dynamic_index_scores_preserve_head_lag (the pin to flip)
Discovery context
Found during adversarial review of the LTM transform-first restructuring on branch roundtrips-track-a (part of the #965 typed-IR arc), while fixing the lag/freeze dynamic-index finding. Verified byte-identical between HEAD f057ef38 and the restructured tree (identical series, NaN included) -- pre-existing, NOT a regression of that work.
Tracking
Part of LTM epic #488. Related but distinct:
Summary
A
PerElementlink-score partial that reads the live source through a DYNAMIC index -- e.g.growth[Region] = pop[Region, idx]withidxa time-varying aux -- lowers the frozen occurrence toPREVIOUS(pop[region·r, PREVIOUS(idx)]). The nestedPREVIOUS(idx)is rewritten through a synthesized capture-helper aux (thebuiltins_visitor.rsmake_temp_argmachinery), and that helper reads an uninitialized value at the FIRST live step, poisoning that one step's link score to NaN. The observed series is0, NaN, 0.4974..., ...-- the NaN is isolated to the single step (the score equation recomputes each step), so it degrades one sample rather than the whole series, but it is a spurious NaN in a compiled link score that consumers (relative-score normalization, discovery ranking, FFI surfaces) will ingest.Where it is pinned
The behavior is deliberately test-pinned as HEAD-preserved:
per_element_dynamic_index_scores_preserve_head_laginsrc/simlin-engine/src/db/ltm_char_tests.rsassertsresults[1]IS NaN, because the NaN is the exact discriminator that the dynamic-index lag is present at all (an un-lagged regression makes the same step finite). The test's comment names this as a pre-existing latent bug tracked separately -- this issue is that tracking. When fixed, that guard must be updated in the same change (the discriminator should become: helper initialized AND lag present).Fix direction
The synthesized
PREVIOUS(idx)capture helper needs a well-defined value at the first live step -- e.g. participate in the initials phase so its first read returnsidx's initial value (matchingPREVIOUS's documented first-step semantics for direct scalar args, which read theprev_valuessnapshot seeded from initials), rather than an uninitialized slot.Components affected
src/simlin-engine/src/builtins_visitor.rs-- thePREVIOUShelper-aux synthesis (make_temp_arg/previous_needs_temp_arg) and/or its initials-phase participationsrc/simlin-engine/src/ltm_augment.rs-- the dynamic-index per-element lowering that emits the nestedPREVIOUS(idx)formsrc/simlin-engine/src/db/ltm_char_tests.rs--per_element_dynamic_index_scores_preserve_head_lag(the pin to flip)Discovery context
Found during adversarial review of the LTM transform-first restructuring on branch
roundtrips-track-a(part of the #965 typed-IR arc), while fixing the lag/freeze dynamic-index finding. Verified byte-identical between HEADf057ef38and the restructured tree (identical series, NaN included) -- pre-existing, NOT a regression of that work.Tracking
Part of LTM epic #488. Related but distinct:
make_temp_argshape limitation (scalar-only helper synthesis for bare arrayed args).